From 96a1c15d9963f8fc30f23a24b699ebf7c2017d36 Mon Sep 17 00:00:00 2001 From: cc Date: Sat, 12 Sep 2026 16:02:21 -0700 Subject: [PATCH 01/12] refactor(grasping): isolate GraspGenX dependencies --- MANIFEST.in | 7 + dimos/experimental/isolated_python/README.md | 15 +- .../example/python/pyproject.toml | 8 +- .../isolated_python/example/python/uv.lock | 2 +- dimos/experimental/isolated_python/module.py | 40 +- .../isolated_python/test_end_to_end.py | 2 +- .../isolated_python/test_module.py | 83 +- .../grasping/grasp_gen_x/demo_inference.py | 107 + .../grasping/grasp_gen_x/fixtures/LICENSE | 163 ++ .../grasping/grasp_gen_x/fixtures/README.md | 11 + .../grasp_gen_x/fixtures/object_cloud.npy | Bin 0 -> 31616 bytes .../grasping/grasp_gen_x/module.py | 101 + .../python/graspgenx_runtime/backend.py} | 8 +- .../python/graspgenx_runtime/runtime.py} | 79 +- .../grasp_gen_x/python/pyproject.toml | 35 + .../grasping/grasp_gen_x/python/uv.lock | 1736 +++++++++++++++++ .../test_module.py} | 35 +- dimos/robot/all_blueprints.py | 2 +- .../manipulators/xarm/blueprints/grasp.py | 2 +- docs/capabilities/manipulation/index.md | 4 +- docs/capabilities/manipulation/xarm-grasp.md | 23 +- pyproject.toml | 26 +- uv.lock | 644 ++---- 23 files changed, 2480 insertions(+), 653 deletions(-) create mode 100644 dimos/manipulation/grasping/grasp_gen_x/demo_inference.py create mode 100644 dimos/manipulation/grasping/grasp_gen_x/fixtures/LICENSE create mode 100644 dimos/manipulation/grasping/grasp_gen_x/fixtures/README.md create mode 100644 dimos/manipulation/grasping/grasp_gen_x/fixtures/object_cloud.npy create mode 100644 dimos/manipulation/grasping/grasp_gen_x/module.py rename dimos/manipulation/grasping/{grasp_gen_x_runtime.py => grasp_gen_x/python/graspgenx_runtime/backend.py} (91%) rename dimos/manipulation/grasping/{grasp_gen_x.py => grasp_gen_x/python/graspgenx_runtime/runtime.py} (61%) create mode 100644 dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml create mode 100644 dimos/manipulation/grasping/grasp_gen_x/python/uv.lock rename dimos/manipulation/grasping/{test_grasp_gen_x.py => grasp_gen_x/test_module.py} (85%) diff --git a/MANIFEST.in b/MANIFEST.in index 3aee0bf04e..9b7daddfd3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -10,6 +10,13 @@ global-exclude .DS_Store # loading a new data type — never go back to per-package globs. recursive-include dimos *.yaml *.yml *.json *.urdf *.html *.css *.js *.svg *.tcss +# GraspGenX runs this project separately; include its manifest and locked sources. +include dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml +include dimos/manipulation/grasping/grasp_gen_x/python/uv.lock +recursive-include dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime *.py +prune dimos/manipulation/grasping/grasp_gen_x/python/.venv +recursive-exclude dimos/manipulation/grasping/grasp_gen_x/fixtures * + # --- Exclusions (must come after the includes above so they win) --- # Test fixtures must never ship. recursive-exclude dimos/agents/fixtures * diff --git a/dimos/experimental/isolated_python/README.md b/dimos/experimental/isolated_python/README.md index a9f172a6df..80f4a37c9a 100644 --- a/dimos/experimental/isolated_python/README.md +++ b/dimos/experimental/isolated_python/README.md @@ -71,9 +71,18 @@ exists, Pixi supplies `uv`. If `uv.lock` exists, dimOS uses `--frozen` and treat the lockfile as the source of truth. Source checkouts make the current dimOS checkout available to the runtime. -Installed hosts let `uv` resolve `dimos`, so the host and runtime versions may -differ. The sibling project's `.python-version` and `requires-python` select its -Python version. +Installed hosts resolve `dimos==`; that exact version must be +available from the configured package source. Local wheel installations can supply +the candidate wheel through `UV_FIND_LINKS`. The sibling project's `.python-version` +and `requires-python` select its Python version. Environments are stored under the DimOS cache directory in +`isolated-python//.venv`, so projects do not share environments. +Preparation also warms the DimOS overlay before starting the readiness deadline. + +For runtime sources shipped inside wheels, use a flat project with +`[tool.uv] package = false` and include its sources, manifest, and lockfile as package +data. Python imports the runtime from the project working directory, without an +editable build writing metadata into `site-packages`. Load models and download +checkpoints in the runtime's `start()`, keeping imports and construction lightweight. The host contract retains the public module name and forwards contract RPCs to a unique internal endpoint. Ordinary dimOS serialization and transport handle RPC diff --git a/dimos/experimental/isolated_python/example/python/pyproject.toml b/dimos/experimental/isolated_python/example/python/pyproject.toml index 91b13fa4df..844646b3cb 100644 --- a/dimos/experimental/isolated_python/example/python/pyproject.toml +++ b/dimos/experimental/isolated_python/example/python/pyproject.toml @@ -1,17 +1,11 @@ -[build-system] -requires = ["setuptools>=70"] -build-backend = "setuptools.build_meta" - [project] name = "example-isolated-python-runtime" version = "0.1.0" requires-python = ">=3.12,<3.13" [tool.uv] +package = false required-environments = [ "sys_platform == 'linux' and platform_machine == 'aarch64'", "sys_platform == 'linux' and platform_machine == 'x86_64'", ] - -[tool.setuptools.packages.find] -where = ["."] diff --git a/dimos/experimental/isolated_python/example/python/uv.lock b/dimos/experimental/isolated_python/example/python/uv.lock index 8b8f523d3c..31c5bc2da2 100644 --- a/dimos/experimental/isolated_python/example/python/uv.lock +++ b/dimos/experimental/isolated_python/example/python/uv.lock @@ -9,4 +9,4 @@ required-markers = [ [[package]] name = "example-isolated-python-runtime" version = "0.1.0" -source = { editable = "." } +source = { virtual = "." } diff --git a/dimos/experimental/isolated_python/module.py b/dimos/experimental/isolated_python/module.py index 6375999e2d..a99ffa966b 100644 --- a/dimos/experimental/isolated_python/module.py +++ b/dimos/experimental/isolated_python/module.py @@ -16,6 +16,8 @@ from __future__ import annotations +from hashlib import sha256 +from importlib.metadata import version import inspect import os from pathlib import Path @@ -26,7 +28,7 @@ import time from typing import Any, ClassVar -from dimos.constants import DIMOS_PROJECT_ROOT +from dimos.constants import CACHE_DIR, DIMOS_PROJECT_ROOT from dimos.core.core import rpc from dimos.core.module import Module from dimos.core.native_module import NativeModule, NativeModuleConfig @@ -45,8 +47,7 @@ def isolated_python_run_command(project: Path, *command: str) -> list[str]: if (DIMOS_PROJECT_ROOT / "pyproject.toml").is_file(): args.extend(("--with-editable", str(DIMOS_PROJECT_ROOT))) else: - # Installed hosts intentionally accept the newest compatible DimOS. - args.extend(("--with", "dimos")) + args.extend(("--with", f"dimos=={version('dimos')}")) args.extend(command) if (project / "pixi.toml").is_file(): return ["pixi", "run", "--executable", *args] @@ -162,24 +163,31 @@ def _runtime_env(self) -> dict[str, str]: env.pop("VIRTUAL_ENV", None) env.pop("UV_PYTHON", None) env.pop("UV_PROJECT_ENVIRONMENT", None) + project_key = sha256(str(self.runtime_project).encode()).hexdigest()[:16] + env["UV_PROJECT_ENVIRONMENT"] = str(CACHE_DIR / "isolated-python" / project_key / ".venv") env.update(self.config.extra_env) return env def _run_prepare(self) -> None: - command = self._prepare_command() - result = subprocess.run( - command, - cwd=self.runtime_project, - env=self._runtime_env(), - capture_output=True, - text=True, - ) - if result.returncode: - output = (result.stdout + "\n" + result.stderr).strip() - raise RuntimeError( - f"Isolated Python environment preparation failed (exit {result.returncode}): " - f"{output[-self.config.output_limit :]}" + # Resolve the DimOS overlay too, before the runtime's readiness deadline. + commands = [ + self._prepare_command(), + isolated_python_run_command(self.runtime_project, "python", "-c", "pass"), + ] + for command in commands: + result = subprocess.run( + command, + cwd=self.runtime_project, + env=self._runtime_env(), + capture_output=True, + text=True, ) + if result.returncode: + output = (result.stdout + "\n" + result.stderr).strip() + raise RuntimeError( + f"Isolated Python environment preparation failed (exit {result.returncode}): " + f"{output[-self.config.output_limit :]}" + ) def _spawn_runtime(self) -> None: parent_read, child_write = os.pipe() diff --git a/dimos/experimental/isolated_python/test_end_to_end.py b/dimos/experimental/isolated_python/test_end_to_end.py index bdbbf28fcc..602b0e5213 100644 --- a/dimos/experimental/isolated_python/test_end_to_end.py +++ b/dimos/experimental/isolated_python/test_end_to_end.py @@ -92,7 +92,7 @@ def test_example_script_exits_after_printing_results() -> None: cwd=repository, capture_output=True, text=True, - timeout=30, + timeout=300, ) assert result.returncode == 0, result.stderr diff --git a/dimos/experimental/isolated_python/test_module.py b/dimos/experimental/isolated_python/test_module.py index cf0ac9d08e..d9b29c1ca7 100644 --- a/dimos/experimental/isolated_python/test_module.py +++ b/dimos/experimental/isolated_python/test_module.py @@ -13,6 +13,7 @@ # limitations under the License. from pathlib import Path +import subprocess import pytest from pytest_mock import MockerFixture @@ -21,6 +22,7 @@ from dimos.experimental.isolated_python.module import ( IsolatedPythonModule, IsolatedPythonModuleConfig, + isolated_python_run_command, ) @@ -78,7 +80,7 @@ def test_uv_lock_enables_frozen_commands(tmp_path: Path, monkeypatch: pytest.Mon module.stop() -def test_installed_host_uses_unversioned_dimos( +def test_installed_host_uses_matching_dimos_version( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: source = tmp_path / "contract.py" @@ -88,6 +90,7 @@ def test_installed_host_uses_unversioned_dimos( (project / "pyproject.toml").touch() installed_root = tmp_path / "site-packages" installed_root.mkdir() + monkeypatch.setattr("dimos.experimental.isolated_python.module.version", lambda _: "9.8.7") monkeypatch.setattr( "dimos.experimental.isolated_python.module.inspect.getfile", lambda _: str(source) ) @@ -98,7 +101,7 @@ def test_installed_host_uses_unversioned_dimos( try: command = module._launch_command(7) - assert command[:4] == ["uv", "run", "--with", "dimos"] + assert command[:4] == ["uv", "run", "--with", "dimos==9.8.7"] assert "--python" not in command finally: module.stop() @@ -123,15 +126,37 @@ def test_pixi_supplies_uv_when_manifest_exists( module.stop() -def test_runtime_environment_uses_sibling_virtualenv( +def test_runtime_environment_uses_project_specific_cache( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: monkeypatch.setenv("VIRTUAL_ENV", "/parent/.venv") + monkeypatch.setenv("UV_PYTHON", "3.10") + monkeypatch.setenv("UV_PROJECT_ENVIRONMENT", "/parent/.venv") + monkeypatch.setattr("dimos.experimental.isolated_python.module.CACHE_DIR", tmp_path / "cache") + project = tmp_path / "python" + project.mkdir() + (project / "pyproject.toml").touch() + monkeypatch.setattr( + "dimos.experimental.isolated_python.module.inspect.getfile", + lambda _: str(tmp_path / "contract.py"), + ) module = Contract(extra_env={"EXAMPLE_SETTING": "configured"}) try: env = module._runtime_env() assert "VIRTUAL_ENV" not in env + assert "UV_PYTHON" not in env + assert Path(env["UV_PROJECT_ENVIRONMENT"]).is_relative_to(tmp_path / "cache") + assert module._runtime_env()["UV_PROJECT_ENVIRONMENT"] == env["UV_PROJECT_ENVIRONMENT"] + other = tmp_path / "other" + (other / "python").mkdir(parents=True) + (other / "python/pyproject.toml").touch() + monkeypatch.setattr( + "dimos.experimental.isolated_python.module.inspect.getfile", + lambda _: str(other / "contract.py"), + ) + assert module._runtime_env()["UV_PROJECT_ENVIRONMENT"] != env["UV_PROJECT_ENVIRONMENT"] assert env["EXAMPLE_SETTING"] == "configured" finally: module.stop() @@ -169,3 +194,55 @@ def test_runtime_build_skips_environment_preparation(mocker: MockerFixture) -> N spawn.assert_not_called() finally: module.stop() + + +@pytest.fixture +def project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path: + runtime = tmp_path / "python" + runtime.mkdir() + (runtime / "pyproject.toml").touch() + (runtime / "uv.lock").touch() + monkeypatch.setattr( + "dimos.experimental.isolated_python.module.inspect.getfile", + lambda _: str(tmp_path / "contract.py"), + ) + return runtime + + +def test_preparation_warms_the_launch_environment(project: Path, mocker: MockerFixture) -> None: + run = mocker.patch( + "dimos.experimental.isolated_python.module.subprocess.run", + return_value=subprocess.CompletedProcess([], 0, "", ""), + ) + module = Contract() + try: + module._run_prepare() + + assert [call.args[0] for call in run.call_args_list] == [ + module._prepare_command(), + isolated_python_run_command(project, "python", "-c", "pass"), + ] + for call in run.call_args_list: + assert call.kwargs["cwd"] == project + assert call.kwargs["env"] == module._runtime_env() + finally: + module.stop() + + +@pytest.mark.parametrize("failure_stage", [0, 1]) +def test_preparation_failure_prevents_launch( + project: Path, mocker: MockerFixture, failure_stage: int +) -> None: + mocker.patch( + "dimos.experimental.isolated_python.module.subprocess.run", + side_effect=[subprocess.CompletedProcess([], 0, "", "")] * failure_stage + + [subprocess.CompletedProcess([], 1, "", "dependency unavailable")], + ) + module = Contract() + spawn = mocker.patch.object(module, "_spawn_runtime") + try: + with pytest.raises(RuntimeError, match="dependency unavailable"): + module.build() + spawn.assert_not_called() + finally: + module.stop() diff --git a/dimos/manipulation/grasping/grasp_gen_x/demo_inference.py b/dimos/manipulation/grasping/grasp_gen_x/demo_inference.py new file mode 100644 index 0000000000..6223cb936c --- /dev/null +++ b/dimos/manipulation/grasping/grasp_gen_x/demo_inference.py @@ -0,0 +1,107 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Explicit CUDA acceptance check; launches only the grasp proposal module.""" + +from __future__ import annotations + +import argparse +from pathlib import Path +import subprocess + +import numpy as np + +from dimos.experimental.isolated_python.module import isolated_python_run_command +from dimos.manipulation.grasping.grasp_gen_x.module import GraspGenXModule +from dimos.msgs.geometry_msgs.Transform import Transform +from dimos.msgs.sensor_msgs.PointCloud2 import PointCloud2 + + +def run(fixture: Path) -> None: + points = np.load(fixture, allow_pickle=False) + cloud = PointCloud2.from_numpy(points, frame_id="camera", timestamp=12.5) + # The production xArm sweep volume, in the model's +Z approach convention. + module = GraspGenXModule( + gripper={ + "extents_open": (0.0889, 0.030, 0.0370), + "offset_open": (0.0, 0.0, 0.1421), + "extents_half_open": (0.0479, 0.030, 0.0370), + "offset_half_open": (0.0, 0.0, 0.1530), + "fingertip_depth": 0.1606, + }, + grasp_frame_to_tcp=( + (0.0, 1.0, 0.0, 0.0), + (-1.0, 0.0, 0.0, 0.0), + (0.0, 0.0, 1.0, 0.172), + (0.0, 0.0, 0.0, 1.0), + ), + ) + try: + module.build() + process = module._process + assert process is not None + subprocess.run( + isolated_python_run_command( + module.runtime_project, + "python", + "-c", + "import sys, torch, torchvision, numpy; " + "assert sys.version_info[:2] == (3, 12); " + "assert torch.__version__ == '2.7.1+cu128', torch.__version__; " + "assert torchvision.__version__ == '0.22.1+cu128', torchvision.__version__; " + "assert torch.version.cuda == '12.8', torch.version.cuda; " + "assert torch.cuda.is_available(), 'CUDA unavailable'; " + "print('Runtime:', sys.executable, 'Torch:', torch.__version__, " + "'NumPy:', numpy.__version__, 'GPU:', torch.cuda.get_device_name())", + ), + cwd=module.runtime_project, + env=module._runtime_env(), + check=True, + timeout=120, + ) + module.start() + result = module.propose_grasps(cloud) + assert 0 < len(result) <= module.config.max_candidates + assert result.header.frame_id == "camera" + assert result.header.timestamp == 12.5 + scores = np.asarray([candidate.score for candidate in result]) + assert np.isfinite(scores).all() + assert np.all(scores[:-1] >= scores[1:]) + for candidate in result: + matrix = Transform.from_pose("camera", candidate.pose).to_matrix() + assert np.isfinite(matrix).all() + rotation = matrix[:3, :3] + assert np.allclose(rotation.T @ rotation, np.eye(3), atol=1e-5) + assert np.isclose(np.linalg.det(rotation), 1.0, atol=1e-5) + # Validation errors must also survive the real RPC boundary. + cloud.frame_id = "" + try: + module.propose_grasps(cloud) + except ValueError as error: + assert "frame_id" in str(error) + else: + raise AssertionError("Invalid cloud was accepted") + print(f"Validated {len(result)} ranked grasps from {len(points)} recorded points") + finally: + module.stop() + assert process.poll() is not None, "Isolated process survived module.stop()" + print("Isolated process stopped") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--fixture", type=Path, default=Path(__file__).parent / "fixtures/object_cloud.npy" + ) + run(parser.parse_args().fixture) diff --git a/dimos/manipulation/grasping/grasp_gen_x/fixtures/LICENSE b/dimos/manipulation/grasping/grasp_gen_x/fixtures/LICENSE new file mode 100644 index 0000000000..9675a9fbc3 --- /dev/null +++ b/dimos/manipulation/grasping/grasp_gen_x/fixtures/LICENSE @@ -0,0 +1,163 @@ +Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + 1. Definitions. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + END OF TERMS AND CONDITIONS + + Copyright 2023-2026 NVIDIA CORPORATION & AFFILIATES + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/dimos/manipulation/grasping/grasp_gen_x/fixtures/README.md b/dimos/manipulation/grasping/grasp_gen_x/fixtures/README.md new file mode 100644 index 0000000000..7c035409c1 --- /dev/null +++ b/dimos/manipulation/grasping/grasp_gen_x/fixtures/README.md @@ -0,0 +1,11 @@ +# Recorded GraspGenX object cloud + +`object_cloud.npy` contains the 2,624 XYZ points from the `pc` field of +`assets/sample_data/object_pc/1741385877_42353.json` in NVlabs/GraspGenX, +revision `b9429097728cb1c430dd78b92edf17ba318aad03`. + +Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. Apache-2.0; see LICENSE. +Modified for this fixture: extracted XYZ only and converted to float32 NumPy format. +The input is a segmented object cloud in metres. Historical model outputs and +colors were omitted. Inference is stochastic; tests validate candidate invariants +rather than comparing against stored predictions. diff --git a/dimos/manipulation/grasping/grasp_gen_x/fixtures/object_cloud.npy b/dimos/manipulation/grasping/grasp_gen_x/fixtures/object_cloud.npy new file mode 100644 index 0000000000000000000000000000000000000000..ff15897cb3c15395eb92ae518c5760a228d89687 GIT binary patch literal 31616 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 RigidTransform: + matrix = np.asarray(value, dtype=float) + if not np.allclose(matrix[3], [0.0, 0.0, 0.0, 1.0], atol=1e-7): + raise ValueError("grasp_frame_to_tcp must be homogeneous") + rotation = matrix[:3, :3] + if not np.allclose(rotation.T @ rotation, np.eye(3), atol=1e-6) or not np.isclose( + np.linalg.det(rotation), 1.0, atol=1e-6 + ): + raise ValueError("grasp_frame_to_tcp rotation must be orthonormal with determinant +1") + return value + + +class GraspGenXError(RuntimeError): + """Base error for model loading and inference failures.""" + + +class GraspGenXModule(IsolatedPythonModule, GraspGenSpec): + """Grasp proposals implemented in a separately locked Python environment.""" + + implementation = "graspgenx_runtime.runtime:_GraspGenXRuntimeModule" + config: GraspGenXConfig + + @rpc + def propose_grasps(self, object_pointcloud: PointCloud2) -> GraspCandidateArray: + """Return ranked TCP poses for an object point cloud in its input frame.""" + raise NotImplementedError diff --git a/dimos/manipulation/grasping/grasp_gen_x_runtime.py b/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/backend.py similarity index 91% rename from dimos/manipulation/grasping/grasp_gen_x_runtime.py rename to dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/backend.py index 34a76c7f2e..dd8a2216e8 100644 --- a/dimos/manipulation/grasping/grasp_gen_x_runtime.py +++ b/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/backend.py @@ -22,7 +22,7 @@ from huggingface_hub import snapshot_download import numpy as np -from dimos.manipulation.grasping.grasp_gen_x import ( +from dimos.manipulation.grasping.grasp_gen_x.module import ( GRASPGENX_MODEL_REPO, GRASPGENX_MODEL_REVISION, GRASPGENX_MODEL_VERSION, @@ -53,12 +53,12 @@ os.environ["GRASPGENX_CHECKPOINT_DIR"] = str(_snapshot_root) os.environ["GRASPGENX_GRIPPER_CFG_DIR"] = str(_snapshot_root) -from graspgenx.grasp_server import ( +from graspgenx.grasp_server import ( # type: ignore[import-not-found] SWEEP_VOLUME_ONLY_BACKBONES, GraspGenXSampler, ) -from graspgenx.utils.checkpoint_io import load_model_cfg -from graspgenx.x_grippers import make_sweep_volume_gripper_info +from graspgenx.utils.checkpoint_io import load_model_cfg # type: ignore[import-not-found] +from graspgenx.x_grippers import make_sweep_volume_gripper_info # type: ignore[import-not-found] _GRIPPER_TYPES = { "parallel_2f": 0, diff --git a/dimos/manipulation/grasping/grasp_gen_x.py b/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/runtime.py similarity index 61% rename from dimos/manipulation/grasping/grasp_gen_x.py rename to dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/runtime.py index 0107da1a32..0946c4e799 100644 --- a/dimos/manipulation/grasping/grasp_gen_x.py +++ b/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/runtime.py @@ -12,18 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Import-safe DimOS adapter for GraspGenX grasp proposals.""" +"""Concrete grasp proposal implementation, loaded only in the isolated process.""" from __future__ import annotations -from typing import TYPE_CHECKING, Annotated, Any, Literal, TypeAlias +from typing import TYPE_CHECKING, Any import numpy as np -from pydantic import Field, FiniteFloat, field_validator from dimos.core.core import rpc -from dimos.core.module import Module, ModuleConfig -from dimos.manipulation.grasping.grasp_gen_spec import GraspGenSpec +from dimos.manipulation.grasping.grasp_gen_x.module import ( + GraspGenXConfig, + GraspGenXError, + GraspGenXModule, +) from dimos.msgs.geometry_msgs.Pose import Pose from dimos.msgs.geometry_msgs.Quaternion import Quaternion from dimos.msgs.geometry_msgs.Vector3 import Vector3 @@ -31,82 +33,21 @@ from dimos.msgs.manipulation_msgs.GraspCandidateArray import GraspCandidateArray from dimos.msgs.sensor_msgs.PointCloud2 import PointCloud2 from dimos.msgs.std_msgs.Header import Header -from dimos.protocol.service.spec import BaseConfig if TYPE_CHECKING: - from dimos.manipulation.grasping.grasp_gen_x_runtime import GraspGenXRuntime - -GRASPGENX_MODEL_REPO = "adithyamurali/GraspGenXModel" -GRASPGENX_MODEL_REVISION = "7c834043c11a11417e31d6d5ea9355801e40a2c1" -GRASPGENX_MODEL_VERSION = "release" - -BoundedExtent = Annotated[FiniteFloat, Field(gt=0.0, le=0.5)] -BoundedOffset = Annotated[FiniteFloat, Field(ge=-0.5, le=0.5)] -PositiveCount = Annotated[int, Field(gt=0, strict=True)] -SweepExtents: TypeAlias = tuple[BoundedExtent, BoundedExtent, BoundedExtent] -SweepOffset: TypeAlias = tuple[BoundedOffset, BoundedOffset, BoundedOffset] -Vector4: TypeAlias = tuple[FiniteFloat, FiniteFloat, FiniteFloat, FiniteFloat] -RigidTransform: TypeAlias = tuple[Vector4, Vector4, Vector4, Vector4] -GripperFamily: TypeAlias = Literal["parallel_2f", "revolute_2f", "revolute_3f"] - -IDENTITY_TRANSFORM: RigidTransform = ( - (1.0, 0.0, 0.0, 0.0), - (0.0, 1.0, 0.0, 0.0), - (0.0, 0.0, 1.0, 0.0), - (0.0, 0.0, 0.0, 1.0), -) - - -class SweepVolumeGripperConfig(BaseConfig): - """Axis-aligned open and half-open sweep-volume description.""" - - extents_open: SweepExtents - offset_open: SweepOffset - extents_half_open: SweepExtents - offset_half_open: SweepOffset - fingertip_depth: BoundedExtent - family: GripperFamily = "parallel_2f" - - -class GraspGenXConfig(ModuleConfig): - """GraspGenX deployment settings, serializable by DimOS blueprints.""" - - gripper: SweepVolumeGripperConfig - grasp_frame_to_tcp: RigidTransform = IDENTITY_TRANSFORM - max_candidates: PositiveCount = 100 - - # Relational matrix properties cannot be expressed through scalar Field constraints. - @field_validator("grasp_frame_to_tcp") - @classmethod - def _validate_rigid_transform(cls, value: RigidTransform) -> RigidTransform: - matrix = np.asarray(value, dtype=float) - if not np.allclose(matrix[3], [0.0, 0.0, 0.0, 1.0], atol=1e-7): - raise ValueError("grasp_frame_to_tcp must be homogeneous") - rotation = matrix[:3, :3] - if not np.allclose(rotation.T @ rotation, np.eye(3), atol=1e-6) or not np.isclose( - np.linalg.det(rotation), 1.0, atol=1e-6 - ): - raise ValueError("grasp_frame_to_tcp rotation must be orthonormal with determinant +1") - return value - - -class GraspGenXError(RuntimeError): - """Base error for model loading and inference failures.""" + from .backend import GraspGenXRuntime def _create_runtime(config: GraspGenXConfig) -> GraspGenXRuntime: # This import is the intentional first-use boundary for the optional GPU runtime. - from dimos.manipulation.grasping.grasp_gen_x_runtime import GraspGenXRuntime + from .backend import GraspGenXRuntime return GraspGenXRuntime(config) -class GraspGenXModule(Module, GraspGenSpec): +class _GraspGenXRuntimeModule(GraspGenXModule): """Direct adapter whose optional runtime is loaded synchronously by ``start``.""" - dedicated_worker = True - config: GraspGenXConfig # type: ignore[assignment] - def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) self._runtime: GraspGenXRuntime | None = None diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml b/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml new file mode 100644 index 0000000000..41d984b60e --- /dev/null +++ b/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml @@ -0,0 +1,35 @@ +[project] +name = "dimos-graspgenx-runtime" +version = "0.1.0" +requires-python = ">=3.12,<3.13" +dependencies = ["graspgenx", "matplotlib>=3.7.1"] + +[tool.uv] +package = false +required-version = ">=0.9.25" +environments = ["sys_platform == 'linux' and platform_machine == 'x86_64'"] +# Keep the previously validated GraspGenX recipe local to this environment. +override-dependencies = [ + "torch==2.7.1", + "torchvision==0.22.1", + "yourdfpy>=0.0.60", + "trimesh>=4.12", + "numpy>=2", + "timm>=1.0.17", + "huggingface-hub>=0.30,<1", + "diffusers>=0.29", + "pyopengl>=3.1.5", +] + +[tool.uv.sources] +graspgenx = { git = "https://github.com/NVlabs/GraspGenX.git", rev = "b9429097728cb1c430dd78b92edf17ba318aad03" } +torch = { index = "pytorch-cu128" } +torchvision = { index = "pytorch-cu128" } + +[[tool.uv.index]] +name = "pytorch-cu128" +url = "https://download.pytorch.org/whl/cu128" +explicit = true + +[tool.uv.extra-build-dependencies] +sharedarray = ["setuptools>=45,<78"] diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock b/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock new file mode 100644 index 0000000000..4ffeddc4dc --- /dev/null +++ b/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock @@ -0,0 +1,1736 @@ +version = 1 +revision = 3 +requires-python = "==3.12.*" +resolution-markers = [ + "platform_machine == 'x86_64' and sys_platform == 'linux'", +] +supported-markers = [ + "platform_machine == 'x86_64' and sys_platform == 'linux'", +] + +[manifest] +overrides = [ + { name = "diffusers", specifier = ">=0.29" }, + { name = "huggingface-hub", specifier = ">=0.30,<1" }, + { name = "numpy", specifier = ">=2" }, + { name = "pyopengl", specifier = ">=3.1.5" }, + { name = "timm", specifier = ">=1.0.17" }, + { name = "torch", specifier = "==2.7.1", index = "https://download.pytorch.org/whl/cu128" }, + { name = "torchvision", specifier = "==0.22.1", index = "https://download.pytorch.org/whl/cu128" }, + { name = "trimesh", specifier = ">=4.12" }, + { name = "yourdfpy", specifier = ">=0.0.60" }, +] + +[[package]] +name = "absl-py" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/4f/d79676ab82f2e42fc3611618139f13a9c4c31d0cff4b486982047679a802/absl_py-2.5.0.tar.gz", hash = "sha256:0c996f25c0490700fadabe6351630f6111534fa0ae252cc6d2014ea3b141135f", size = 118119, upload-time = "2026-07-03T10:57:48.157Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/0a/a10b45aab35b175aded078a462dc8d0c698f5b13946e7cb0869097b78bb6/absl_py-2.5.0-py3-none-any.whl", hash = "sha256:0f17b89f2a4eaaedc4f28c622998aa690564b3012a396a4ffad0821007fe03ba", size = 137410, upload-time = "2026-07-03T10:57:46.735Z" }, +] + +[[package]] +name = "addict" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/ef/fd7649da8af11d93979831e8f1f8097e85e82d5bfeabc8c68b39175d8e75/addict-2.4.0.tar.gz", hash = "sha256:b3b2210e0e067a281f5646c8c5db92e99b7231ea8b0eb5f74dbdf9e259d4e494", size = 9186, upload-time = "2020-11-21T16:21:31.416Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/00/b08f23b7d7e1e14ce01419a467b583edbb93c6cdb8654e54a9cc579cd61f/addict-2.4.0-py3-none-any.whl", hash = "sha256:249bb56bbfd3cdc2a004ea0ff4c2b6ddc84d53bc2194761636eb314d5cfa5dfc", size = 3832, upload-time = "2020-11-21T16:21:29.588Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757, upload-time = "2026-07-01T17:11:55.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038, upload-time = "2026-07-01T17:11:54.055Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "aiosignal", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "attrs", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "frozenlist", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "multidict", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "propcache", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "yarl", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/d9/22ce5786ac0c1653ae8b6c23bded02c1686d11f0dbb45b31ce128e0df985/aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc", size = 7971213, upload-time = "2026-07-23T01:57:27.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/b7/7cd31f29d6055bd711ae6e669367fba6f5ae9de463910a793e30556a8db7/aiohttp-3.14.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42", size = 1792122, upload-time = "2026-07-23T01:54:15.752Z" }, + { url = "https://files.pythonhosted.org/packages/e0/eb/aad34e897e668424d6e995da5dff8a4a09af93363d3392488772957a63aa/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19", size = 1768629, upload-time = "2026-07-23T01:54:40.103Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" }, +] + +[[package]] +name = "antlr4-python3-runtime" +version = "4.9.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/38/7859ff46355f76f8d19459005ca000b6e7012f2f1ca597746cbcd1fbfe5e/antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b", size = 117034, upload-time = "2021-11-06T17:52:23.524Z" } + +[[package]] +name = "anyio" +version = "4.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/d2/f4d173e22df740bc37b1db102b386ba719b66e95b0f0d751f556b387e6d2/anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94", size = 276966, upload-time = "2026-09-05T10:42:39.44Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "braceexpand" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/93/badd4f5ccf25209f3fef2573073da9fe4a45a3da99fca2f800f942130c0f/braceexpand-0.1.7.tar.gz", hash = "sha256:e6e539bd20eaea53547472ff94f4fb5c3d3bf9d0a89388c4b56663aba765f705", size = 7777, upload-time = "2021-05-07T13:49:07.323Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/93/e8c04e80e82391a6e51f218ca49720f64236bc824e92152a2633b74cf7ab/braceexpand-0.1.7-py2.py3-none-any.whl", hash = "sha256:91332d53de7828103dcae5773fb43bc34950b0c8160e35e0f44c4427a3b85014", size = 5923, upload-time = "2021-05-07T13:49:05.146Z" }, +] + +[[package]] +name = "certifi" +version = "2026.7.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/3f/143b048436775b0f76ac3eec145c019e8173ccc2885c8f20319b996d5e83/charset_normalizer-3.5.1.tar.gz", hash = "sha256:6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3", size = 171764, upload-time = "2026-08-15T08:20:44.807Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/89/bb5108dc6c3651dca963f2b0a3ba19bbcb370c94e1b6d3e0e844a58e6dca/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08", size = 248801, upload-time = "2026-08-15T08:17:17.683Z" }, + { url = "https://files.pythonhosted.org/packages/b7/52/643d11ffd60e9ac2fd1fb87e167a19285b9eefeff4a40e63c87cbfbeab36/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba501e667c17d8411f98e67a022d9604ef179aff0e459b7e292c796837c13573", size = 250562, upload-time = "2026-08-15T08:17:27.971Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2f/fe3f187327aac18e2d54e9d2b08e15d27bf9b642d9e51c219f130fc34d1a/charset_normalizer-3.5.1-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99", size = 253057, upload-time = "2026-08-15T08:19:52.654Z" }, + { url = "https://files.pythonhosted.org/packages/80/c2/a7379b840292d0c1ab9fbd17d1f3967aa81794dc95bc74be8999d7fedcf7/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d", size = 254846, upload-time = "2026-08-15T08:20:10.727Z" }, + { url = "https://files.pythonhosted.org/packages/cc/61/d01fc49b8dea277640b55a9e15960dbca9fdc8c9fde18e572d39c59f4019/charset_normalizer-3.5.1-py3-none-any.whl", hash = "sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6", size = 68658, upload-time = "2026-08-15T08:20:43.306Z" }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, +] + +[[package]] +name = "config-path" +version = "1.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/32/02/75ee14911378497eb3bb1bfa576cd99bc88ecf0d9f60671721a91dbecaaf/config-path-1.0.5.tar.gz", hash = "sha256:ed17ad1a0cbdadb2781443a27a624057138611416885acb3fb41132a6d559d77", size = 11215, upload-time = "2023-10-03T19:41:43.639Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/51/eec3b34b71799668009107b48fa146b9b9d347f60749491ca77a85ccb94c/config_path-1.0.5-py3-none-any.whl", hash = "sha256:7a69fbfdacb95d7591621e67265a87960d54d56abffe95d9f93acd342f926292", size = 8800, upload-time = "2023-10-03T19:41:42.129Z" }, +] + +[[package]] +name = "contourpy" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/5a/a55177dd22553a277388e8a1b3220e92de91bacb28356cdc73caa240121d/contourpy-1.4.0.tar.gz", hash = "sha256:20156f5a1ac4f8ce02656e39a61e82164a3d359796dc8026f75b062783d500e1", size = 13323726, upload-time = "2026-09-11T19:05:05.808Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/f9/b6831508960d559581c448532ba4df312217072975486dbf2b24fcc5b76f/contourpy-1.4.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:875f42444c9cf48d56f724f2637e60d0f73b3b12c9041e1484580a233edf9591", size = 383243, upload-time = "2026-09-11T19:02:58.638Z" }, + { url = "https://files.pythonhosted.org/packages/d8/79/d68b1ce8539e4071518fed9523f558398f34dcd078b8927b109c72dad2ef/contourpy-1.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3863ef2e2b13fe93f8c0ebb08ee400cb07153b8b0e91c5acb26e4537f283634f", size = 1427243, upload-time = "2026-09-11T19:03:03.866Z" }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "cython" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/d8/4981ef716ad0e3ff0d3ef383aefc6b03c4a88dee33b272bf8e0d833001ca/cython-3.3.0.tar.gz", hash = "sha256:eed0d93fbca7087f143b42c34b05a825849bdf17f101572c2105acfa49aa88b8", size = 3727515, upload-time = "2026-08-22T05:16:39.493Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/4e/6b1c5a4e6bbe1726104de007aa2fdf01a3e2e386b4ec93c7be5f5085d53f/cython-3.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:428fafed98ea26927000a287b4dfc9ef07339f56656a5329a34eaa593f79a4f8", size = 3412225, upload-time = "2026-08-22T05:17:12.281Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e8/aa7b4f3a28d6e8117c76e2cf78a0df7a503486cdf7243c5b53200c9533a1/cython-3.3.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:311a016369adfd1e0015c4f9819168fc0e518451d7efb4435c30d65a3a26d52b", size = 3265110, upload-time = "2026-08-22T05:17:52.577Z" }, + { url = "https://files.pythonhosted.org/packages/bf/77/67b0b24e45073a699610e50f00c18474ff9b09ea29ecc95083bdf5e60acd/cython-3.3.0-py3-none-any.whl", hash = "sha256:9b24b5c8cd536946b62086fcafee6d5509d3f549f72d553d2336af87ffbe0da1", size = 1349151, upload-time = "2026-08-22T05:16:36.741Z" }, +] + +[[package]] +name = "diffusers" +version = "0.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "httpx", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "huggingface-hub", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "importlib-metadata", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pillow", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "regex", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "requests", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "safetensors", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/32/b9c6a198812e2a7d2c2828a850dba384306f9af285eac23e35e89d5b008a/diffusers-0.40.0.tar.gz", hash = "sha256:49f112ce52ff6d332ab68afec01f79a53e6489b42cb69104444c98b4a2f64af8", size = 4886420, upload-time = "2026-08-20T14:30:40.923Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/df/ffb593ebed2a068d2d6be44261283f39a6b809c0fcdfdcafbd448cbeec77/diffusers-0.40.0-py3-none-any.whl", hash = "sha256:5b5da7c3ddb62152fa4afc577f02e050af688c797375c34fb2d01006da3f3541", size = 5911654, upload-time = "2026-08-20T14:30:38.422Z" }, +] + +[[package]] +name = "dimos-graspgenx-runtime" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "graspgenx", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "matplotlib", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] + +[package.metadata] +requires-dist = [ + { name = "graspgenx", git = "https://github.com/NVlabs/GraspGenX.git?rev=b9429097728cb1c430dd78b92edf17ba318aad03" }, + { name = "matplotlib", specifier = ">=3.7.1" }, +] + +[[package]] +name = "dm-control" +version = "1.0.46" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "dm-env", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "dm-tree", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "glfw", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "labmaze", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "lxml", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "mujoco", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "protobuf", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyopengl", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyparsing", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "requests", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "scipy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tqdm", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/30/f8613e2345bda75eabd37549dbb3b58ac0d7ee2027468d5b95a70792d760/dm_control-1.0.46.tar.gz", hash = "sha256:0bc1aa81f7a118c178f19c040d3a8ce8f48ab848ebf7ec69e738861abff2a8dd", size = 56281558, upload-time = "2026-09-09T18:00:29.886Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/14/f678a816447389b70f4d95d18fa5972dd18984c6f1902e6a9f93fc0db914/dm_control-1.0.46-py3-none-any.whl", hash = "sha256:be98ff8a88f26796caa0eb4ac11af4f19076c34fd9ab101b38f9d4bd4016c09a", size = 56452220, upload-time = "2026-09-09T18:00:25.459Z" }, +] + +[[package]] +name = "dm-env" +version = "1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "dm-tree", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/c9/93e8d6239d5806508a2ee4b370e67c6069943ca149f59f533923737a99b7/dm-env-1.6.tar.gz", hash = "sha256:a436eb1c654c39e0c986a516cee218bea7140b510fceff63f97eb4fcff3d93de", size = 20187, upload-time = "2022-12-21T00:25:29.306Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/7e/36d548040e61337bf9182637a589c44da407a47a923ee88aec7f0e89867c/dm_env-1.6-py3-none-any.whl", hash = "sha256:0eabb6759dd453b625e041032f7ae0c1e87d4eb61b6a96b9ca586483837abf29", size = 26339, upload-time = "2022-12-21T00:25:37.128Z" }, +] + +[[package]] +name = "dm-tree" +version = "0.1.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "attrs", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "wrapt", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/66/a3ec619d22b6baffa5ab853e8dc6ec9d0c837127948af59bb15b988d7312/dm_tree-0.1.10.tar.gz", hash = "sha256:22f37b599e01cc3402a17f79c257a802aebd8d326de05b54657650845956208a", size = 35748, upload-time = "2026-03-31T17:35:39.03Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/eb/1d55c679cee9a54e552480d308535753c72e2250cf720d7aa777bff2a4fe/dm_tree-0.1.10-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:012c2b376e88d3685c73a4b5c23be41fe933e14e380dcd90172971690b0e02d2", size = 186506, upload-time = "2026-03-31T17:35:17.593Z" }, +] + +[[package]] +name = "etils" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/ce/6e067242fde898841922ac6fc82b0bb2fe35c38e995880bdffdfbe30182a/etils-1.14.0.tar.gz", hash = "sha256:8136e7f4c4173cd0af0ca5481c4475152f0b8686192951eefa60ee8711e1ede4", size = 108127, upload-time = "2026-03-04T17:41:36.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/3d/589663aeeacd59bb2f3e8596bfd3e81cf0fb18d70bb433199041f469771b/etils-1.14.0-py3-none-any.whl", hash = "sha256:b5df7341f54dbe1405a4450b2741207b4a8c279780402b45f87202b94dfc52b4", size = 172934, upload-time = "2026-03-04T17:41:35.01Z" }, +] + +[package.optional-dependencies] +epath = [ + { name = "fsspec", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "zipp", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] + +[[package]] +name = "filelock" +version = "3.32.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/46/126b1831dca12060d4a8296bf9c4fe5c93c4f22197fa239cb0cc82042bba/filelock-3.32.6.tar.gz", hash = "sha256:a3f55a18af3652a94d8f47d6055df434f254ca1d02ef2524850c6d249ca2512c", size = 225172, upload-time = "2026-09-08T22:57:11.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/06/4f138f618dbea66803291274f228f01daf29f306fe8b96bc30dab765df75/filelock-3.32.6-py3-none-any.whl", hash = "sha256:3f16ecd0117feae0dfc147e8c62eb5daeccd8bd800378c3ddf416de9b4feb6b1", size = 100189, upload-time = "2026-09-08T22:57:10.182Z" }, +] + +[[package]] +name = "fonttools" +version = "4.65.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/77/51/d63c7e52163ac14393a35bd14bd7c0da95f8f74be5d7cc988092f9965129/fonttools-4.65.0.tar.gz", hash = "sha256:762ba5431358d0dbd4a01982484a1d494fb267e91f974cdcf20b80eab8560f6f", size = 3674467, upload-time = "2026-09-10T15:35:54.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/09/de2c0c20a42c18e565a2617932beb08c06697bbdd0d3f62b108262e11583/fonttools-4.65.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:05595385ae99f4b9626cebb973bf171b8fe38a8f40708e6e42abba0ed7537778", size = 5402463, upload-time = "2026-09-10T15:33:54.907Z" }, + { url = "https://files.pythonhosted.org/packages/1f/0d/2116763ade7e71e0e5d421babe1785d745be9b3d605bf914792ce1c97f79/fonttools-4.65.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:924d06e6130429168318db71c40174a765ad016fc4b56ca811287e3d7373b3a6", size = 5524117, upload-time = "2026-09-10T15:34:00.021Z" }, + { url = "https://files.pythonhosted.org/packages/e6/35/f894ceb867118c0261d0f69a9bd516b045a3754238f76c88a49513ac7a83/fonttools-4.65.0-py3-none-any.whl", hash = "sha256:3060b8c1fc2329fa20265b7c138614143ea7c1624e26c5c180c76aeb74deae6f", size = 1196441, upload-time = "2026-09-10T15:35:52.347Z" }, +] + +[[package]] +name = "freetype-py" +version = "2.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/9c/61ba17f846b922c2d6d101cc886b0e8fb597c109cedfcb39b8c5d2304b54/freetype-py-2.5.1.zip", hash = "sha256:cfe2686a174d0dd3d71a9d8ee9bf6a2c23f5872385cf8ce9f24af83d076e2fbd", size = 851738, upload-time = "2024-08-29T18:32:26.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/36/853cad240ec63e21a37a512ee19c896b655ce1772d803a3dd80fccfe63fe/freetype_py-2.5.1-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:289b443547e03a4f85302e3ac91376838e0d11636050166662a4f75e3087ed0b", size = 1043856, upload-time = "2024-08-29T18:32:20.565Z" }, + { url = "https://files.pythonhosted.org/packages/2a/1b/161d3a6244b8a820aef188e4397a750d4a8196316809576d015f26594296/freetype_py-2.5.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:3c1aefc4f0d5b7425f014daccc5fdc7c6f914fb7d6a695cc684f1c09cd8c1660", size = 1106792, upload-time = "2024-08-29T18:32:23.134Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/78/f34251dadb8f3921264a1d9b8946f5e542014ee2614b285261b4e40e6775/fsspec-2026.7.0.tar.gz", hash = "sha256:c803c40f4cf860b49dea58ee3e1c33cb9c790520e233537e1340049f89b82a88", size = 317040, upload-time = "2026-07-28T16:34:51.052Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/3c/6a2bf344106328fd04963664a60b9bb6496fc25df8e962fcdc1367285fb9/fsspec-2026.7.0-py3-none-any.whl", hash = "sha256:b57ddbafedfaef7018c1ecab32aa200a9d7ca26b77965f64e48b70061249d279", size = 206583, upload-time = "2026-07-28T16:34:49.538Z" }, +] + +[[package]] +name = "glfw" +version = "2.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/62/096058bcb4b4fb28f7ecd28fb048f07969d90b243c417af5f6d09d45a0c2/glfw-2.10.2.tar.gz", hash = "sha256:5d2cf97c66bc42a6b583be0e307eae5a3945438322e2ed0c5e4f14dc251d693d", size = 36307, upload-time = "2026-07-21T14:42:36.732Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/44/37aeed50c581c76e1c5bb83b696a06f98f8ecc979ed8564dde3eef908e8b/glfw-2.10.2-py2.py3-none-manylinux2014_x86_64.whl", hash = "sha256:82daf1f87b2a48815637dc6f6720d4a55a57ba1d3683322dc20dc28065754f97", size = 246951, upload-time = "2026-07-21T14:42:29.107Z" }, + { url = "https://files.pythonhosted.org/packages/69/84/1b98671314ea5f40397586b6cd14db913de3196333be558fdc177e61708f/glfw-2.10.2-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:b860de3ca0686182483f98f3ddd12e660acf25b3e0d521450ec9a3f999f72a65", size = 248490, upload-time = "2026-07-21T14:42:32.377Z" }, +] + +[[package]] +name = "graspgenx" +version = "1.0.0" +source = { git = "https://github.com/NVlabs/GraspGenX.git?rev=b9429097728cb1c430dd78b92edf17ba318aad03#b9429097728cb1c430dd78b92edf17ba318aad03" } +dependencies = [ + { name = "addict", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "diffusers", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "h5py", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "huggingface-hub", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "hydra-core", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "imageio", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "matplotlib", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyopengl", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyrender", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pytest", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyyaml", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "scene-synthesizer", extra = ["recommend"], marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "scikit-learn", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "scipy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "sharedarray", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tensorboard", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tensorboardx", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tensordict", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "timm", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torch-geometric", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torchvision", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tqdm", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "transformers", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "trimesh", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "urdfpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "viser", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "webdataset", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "yapf", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "yourdfpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] + +[[package]] +name = "grpcio" +version = "1.83.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/b1/46539f5050d7c316a13396d185451f95084a74ddc68b12d818595bef0377/grpcio-1.83.1.tar.gz", hash = "sha256:9cee6fcbf2eb57c4b49451787bfa87be8efc1ca02a0b327dd4b54d44502e362b", size = 13445033, upload-time = "2026-08-28T07:09:11.464Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/19/9fc702e31a631262d7a752fa699f6022821e707fefc8bff49b1550a57729/grpcio-1.83.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:72578aa07a4008f17521ef52debcc3acfd1e2c5426243bc3ffb56a38bfe610b7", size = 7040936, upload-time = "2026-08-28T07:08:15.963Z" }, + { url = "https://files.pythonhosted.org/packages/3d/fa/f0586c56bdfb8a7a2adda01e0ac2413447cde3141ab09411a5d5afdcffd3/grpcio-1.83.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e703effe3ae779925c82ac24fdb82cf4105e1096810151ed9501c5f34546b9c", size = 7984321, upload-time = "2026-08-28T07:08:22.114Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "h5py" +version = "3.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526, upload-time = "2026-03-06T13:49:08.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/e9/1a19e42cd43cc1365e127db6aae85e1c671da1d9a5d746f4d34a50edb577/h5py-3.16.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:dfc21898ff025f1e8e67e194965a95a8d4754f452f83454538f98f8a3fcb207e", size = 5405250, upload-time = "2026-03-06T13:48:09.628Z" }, + { url = "https://files.pythonhosted.org/packages/51/d7/ab693274f1bd7e8c5f9fdd6c7003a88d59bedeaf8752716a55f532924fbb/h5py-3.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b2c02b0a160faed5fb33f1ba8a264a37ee240b22e049ecc827345d0d9043074", size = 5419216, upload-time = "2026-03-06T13:48:13.322Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/ab/522a2ab67f27971a9d48ca666d4fca85ef7d5282d142e31fd087e27b1bbe/hf_xet-1.6.0.tar.gz", hash = "sha256:2e58454a340b3556dfa4972d5451aff4fba8dd42a236600ba1a1d2b1514f0fef", size = 920527, upload-time = "2026-08-03T22:33:13.243Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/4e/a28359bf1c1ecf11eba22123168c138698f7cb576ac678f5a2e16cd5da08/hf_xet-1.6.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d62671bb130879cef0ee4c9ebe47a14af6c66ec53e6d84dc15936e5ffdfac82f", size = 4464663, upload-time = "2026-08-03T22:33:03.802Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5f/311725e2a905534dfee2dcb5b08414f249147f1f12252bfc2bd24caa075c/hf_xet-1.6.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4f71cba6129110c3374a33f919001ff130488fc23553698e34cc1c2a1198c", size = 4675937, upload-time = "2026-08-03T22:33:08.616Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "h11", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "certifi", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "httpcore", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "idna", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "fsspec", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "hf-xet", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyyaml", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "requests", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tqdm", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/b7/8cb61d2eece5fb05a83271da168186721c450eb74e3c31f7ef3169fa475b/huggingface_hub-0.36.2.tar.gz", hash = "sha256:1934304d2fb224f8afa3b87007d58501acfda9215b334eed53072dd5e815ff7a", size = 649782, upload-time = "2026-02-06T09:24:13.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/af/48ac8483240de756d2438c380746e7130d1c6f75802ef22f3c6d49982787/huggingface_hub-0.36.2-py3-none-any.whl", hash = "sha256:48f0c8eac16145dfce371e9d2d7772854a4f591bcb56c9cf548accf531d54270", size = 566395, upload-time = "2026-02-06T09:24:11.133Z" }, +] + +[[package]] +name = "hydra-core" +version = "1.3.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "antlr4-python3-runtime", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "omegaconf", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/03/adfa2bf3a9eef568a85f2e7cf18ce1938e7aa0b5257c432e5eb48f89960a/hydra_core-1.3.6.tar.gz", hash = "sha256:8c4c215b6cfba68e9a4e76f565bd7399fc2a86b93cfe8cb7328b046834dfbee5", size = 3272413, upload-time = "2026-08-29T14:36:08.092Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/fd/612cc7905787100dc9225ae6ad71b42b60cbc23cd56ca55afcdd1e000f81/hydra_core-1.3.6-py3-none-any.whl", hash = "sha256:55951f49c7e19c29f4281cce2eeaa6e73ef6c5620093a4efb402a7e4b839fc38", size = 163055, upload-time = "2026-08-29T14:36:06.385Z" }, +] + +[[package]] +name = "idna" +version = "3.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/f7/abb373e5757eaec4b922b92f97ec8d6d7e057cf06778247604fbc4e7c3f3/idna-3.19.tar.gz", hash = "sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15", size = 215237, upload-time = "2026-08-18T05:14:24.27Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl", hash = "sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", size = 68550, upload-time = "2026-08-18T05:14:22.343Z" }, +] + +[[package]] +name = "imageio" +version = "2.37.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pillow", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/62/aa770a9307508d2a2a2c62d536a49347bffe9e55322db27838d3c93d0b07/imageio-2.37.4.tar.gz", hash = "sha256:e45cbc5e83502047fb138f7f585f7f105a136a57eea5f4b3cfc6ce1b52720bd3", size = 390173, upload-time = "2026-07-20T05:26:11.369Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/2d/ca050652104bab2cf55e569db2a178b1b61cb041fef28307f2db383f6d9f/imageio-2.37.4-py3-none-any.whl", hash = "sha256:1ab2e22c8debf700f24c3ac43e8f95f3b3a8110c83b93411e97b4b0b2cd1c7e6", size = 318000, upload-time = "2026-07-20T05:26:09.874Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "9.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/7e/1e7e8dc30634b93ebb3d58a3dea569ad146e656218d3960ab04f62047b29/importlib_metadata-9.0.1.tar.gz", hash = "sha256:ab830580bc0ef3db61ce8fae716389e5462b67e033018bab6d8f80ef17172f99", size = 59124, upload-time = "2026-08-28T15:30:34.646Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/55/ecca97ae19075f1fac62def77731e7f535e6c1fb8f92ff08160c5e6dade8/importlib_metadata-9.0.1-py3-none-any.whl", hash = "sha256:bba5600596a7e21f3eef53281cf28d6a5195634d2f2b78ff9501a3272c6eaab0", size = 27920, upload-time = "2026-08-28T15:30:33.433Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "joblib" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cloudpickle", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/1d/537ab090f302b838943a1b56497dd53059b9a9b46a074936470173a2e207/joblib-1.6.0.tar.gz", hash = "sha256:2ccc96785b12046c08fd6d55839c12857831b54a3c1673ffadd2f04bfc4eda03", size = 327903, upload-time = "2026-08-31T09:39:04.122Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/53/84099323c2ec4be98d935f63c033ac4151ee83836ca1050ede3b3aadf155/joblib-1.6.0-py3-none-any.whl", hash = "sha256:3dbbf9f6e4b592a2357b854608e980fe6390d131d7a82f011a377ef2ebef7aba", size = 306115, upload-time = "2026-08-31T09:39:02.298Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/07/bd78e6a8fae171ea041ef5bba3ed21a003522fa088834b069b1909981f30/kiwisolver-1.5.1.tar.gz", hash = "sha256:f1303ef2eec81262a4b708c3e858afe58d7c75ad91c1c05266eda7673369859a", size = 104395, upload-time = "2026-08-28T10:28:27.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/f4/dadfec469313c7f428efa7e84b4aba9732f813c13ea7131a24b7b008ef57/kiwisolver-1.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34633ecf50d16187ab8e5528b7a2530f2feb4e23f300db4672538b51cfc5cd38", size = 1477929, upload-time = "2026-08-28T10:25:31.495Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d3/024208ec1079d273f1047468d1bdffbf38bb75b7b268090fd3a0301b9d9a/kiwisolver-1.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b03af77d77e50edba2030fd5f7c352ff209314b09030a3cba7c14edf9a09a444", size = 2295200, upload-time = "2026-08-28T10:25:46.984Z" }, + { url = "https://files.pythonhosted.org/packages/16/45/c37a21ad5c0ab581a93c55ad544721aaa1f0ae94edb29c6a678a23d013e6/kiwisolver-1.5.1-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:072bdb15a3c19a5b5dbc8f8fb1f4e1884bf4f3507eeb4cc6334401274d37a5c0", size = 194292, upload-time = "2026-08-28T10:28:11.06Z" }, +] + +[[package]] +name = "labmaze" +version = "1.0.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/0a/139c4ae896b9413bd4ca69c62b08ee98dcfc78a9cbfdb7cadd0dce2ad31d/labmaze-1.0.6.tar.gz", hash = "sha256:2e8de7094042a77d6972f1965cf5c9e8f971f1b34d225752f343190a825ebe73", size = 4670455, upload-time = "2022-12-05T18:42:43.566Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9c/1c928d0f5a20e4b9544d564e43ecda785f09a29ecbaa37f4e70989d0d4bd/labmaze-1.0.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d17abb69d4dfc56183afb5c317e8b2eaca0587abb3aabd2326efd3143c81f4e", size = 4875122, upload-time = "2023-10-04T17:08:11.069Z" }, +] + +[[package]] +name = "lxml" +version = "6.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/ad/28ecd7cb894d172f3c9c80a075eeeb2017ac62e3632cee05a5f9493547eb/lxml-6.1.3.tar.gz", hash = "sha256:45222d94ddd511536f3b2f7d9deae3b2339b4ce0f075f1ca25703b07cad9dd21", size = 4211198, upload-time = "2026-09-02T14:48:02.287Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/36/aa413bc214dc4f785ad2b2ddd8cc99aae7062d49ab155e91e6011af00daf/lxml-6.1.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:379f8a75cf6eb7eef0af074b55f49ab73b868388a98de14646abcdfa4564bb11", size = 5105247, upload-time = "2026-09-02T14:47:36.734Z" }, + { url = "https://files.pythonhosted.org/packages/0a/20/e022dbc6b4753a9bc9fc5fb28a27163430c1731b9913997f6544c1b2518c/lxml-6.1.3-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:909f4e927bb051f7740d6367285fc60cdcfdaf0258c2dba4ff5ba7eadadc250c", size = 5244675, upload-time = "2026-09-02T14:47:47.635Z" }, + { url = "https://files.pythonhosted.org/packages/da/ee/2c016fbceb3778137459292538d9dfa7e3ad9070fe409c15254ddd90d2cc/lxml-6.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c55e71a9b1db1f107efb60da49c093689b74c5c31a708e5379e2fd9439d4fbb5", size = 5267340, upload-time = "2026-09-02T14:48:08.374Z" }, +] + +[[package]] +name = "markdown" +version = "3.10.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/6f/da4c6aea59b3001f2e8c0ec7497475aadaf3b021c10cab5b2858f0f32b26/markdown-3.10.3.tar.gz", hash = "sha256:3589362618f743188b4d955b874402bc814f4f83f544dc207719f4baa7d9c45f", size = 372596, upload-time = "2026-07-30T19:05:29.005Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/69/4a5af2bc115a9a33fefe51709749de8262be3f9ba063d1753a837cdbc49c/markdown-3.10.3-py3-none-any.whl", hash = "sha256:fa6c92a00a4a3c98b22728c64a935ae1928250ae65058a6ded814d2cc29a4cea", size = 110757, upload-time = "2026-07-30T19:05:27.883Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.11.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "cycler", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "fonttools", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "kiwisolver", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pillow", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyparsing", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "python-dateutil", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/c8/9aa712a0afb882649424dd8de8ad9aa6235e796e84c6052e8f6dc1598d0d/matplotlib-3.11.2.tar.gz", hash = "sha256:cec596316640f2b394b8f0daa0ea61a8eae82d017b620b9f202befb972a59ea4", size = 32660610, upload-time = "2026-09-11T19:05:31.214Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/90/2b3fd67ee273163faeda6d514be70b5596eaa0fd77b60ffc294ad0b34f5f/matplotlib-3.11.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:df4f7784aca81a94f254c0a2767d592ee25f407e488f5fa7203e51093fb6ca27", size = 9860353, upload-time = "2026-09-11T19:03:25.049Z" }, + { url = "https://files.pythonhosted.org/packages/84/39/02e21b74f7439bd643d717ea846006d46e309e6d29b632b57751265311d6/matplotlib-3.11.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3aa4b8516fd26659e4363abbf317c703d9116496c5db2e9d0609a2866dd39dd2", size = 10810580, upload-time = "2026-09-11T19:03:31.402Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "msgspec" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/60/f79b9b013a16fa3a58350c9295ddc6789f2e335f36ea61ed10a21b215364/msgspec-0.21.1.tar.gz", hash = "sha256:2313508e394b0d208f8f56892ca9b2799e2561329de9763b19619595a6c0f72c", size = 319193, upload-time = "2026-04-12T21:44:50.394Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/d1/d4cd9fe89c7d400d7a18f86ccc94daa3f0927f53558846fcb60791dce5d6/msgspec-0.21.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21995e74b5c598c2e004110ad66ec7f1b8c20bf2bcf3b2de8fd9a3094422d3ff", size = 225025, upload-time = "2026-04-12T21:44:11.191Z" }, + { url = "https://files.pythonhosted.org/packages/b4/68/04d7a8f0f786545cf9b8c280c57aa6befb5977af6e884b8b54191cbe44b3/msgspec-0.21.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ef3ec2296248d1f8b9231acb051b6d471dfde8f21819e86c9adaaa9f42918521", size = 227303, upload-time = "2026-04-12T21:44:13.709Z" }, +] + +[[package]] +name = "mujoco" +version = "3.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "etils", extra = ["epath"], marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "glfw", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyopengl", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/f6/0c581b100dfc71bc999844685e3d33cfe972f7fed24b62c47b346be7a0fa/mujoco-3.13.0.tar.gz", hash = "sha256:539175d5db25d19ec145170fa264784a626c5ecb227f78d1877e35a0a7955f69", size = 1213045, upload-time = "2026-09-09T15:39:54.985Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/14/585fc852dadf2d73250b45e9d5845624270ca526b723bc2a1c24e1305006/mujoco-3.13.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4ad24d902887dd9ec1ec5c7149a2d3cf62fb3f6e652a82d113e1ec3d6308ed5", size = 20378803, upload-time = "2026-09-09T15:38:49.848Z" }, +] + +[[package]] +name = "multidict" +version = "6.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/95/989c1b5ca17b72128661530cd6e351a0a83cda9a4d6c036e9ed976c18931/multidict-6.8.0.tar.gz", hash = "sha256:5cd4637ce76312ba1e05eb9c5193fec231f64fee0944e135fa1e951242355b37", size = 122412, upload-time = "2026-09-09T13:57:57.967Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/c7/4544cc02e45bbfac4d8788b05379bb360021fd8c53fa74b0f624126ac188/multidict-6.8.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:003a3bddb32915c3f67096ea41d24e53edf710edb65a1f5d0c70ab40b0e4d20b", size = 287410, upload-time = "2026-09-09T13:54:01.652Z" }, + { url = "https://files.pythonhosted.org/packages/08/7e/7b7cd611fd94bf2f6bd16244c50495867ba394d5baaf8e6e487d39494ab3/multidict-6.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad474c11d851b6fc97cb625e4822bc0cbd567fc07dc2602e28faec5a36b42bbb", size = 281653, upload-time = "2026-09-09T13:54:15.174Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ee/be4e1a4b7a2b27f4fb6936510d4bebcb41b0562c946930ad26916e069cf9/multidict-6.8.0-py3-none-any.whl", hash = "sha256:75daa15ca16d6285eb2e104b2f05ee6f8d9836c68da3ce5c85f615a0450eed0e", size = 16297, upload-time = "2026-09-09T13:57:56.106Z" }, +] + +[[package]] +name = "narwhals" +version = "2.26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/68/5351e34623d253423240ea7de3f8fc74fa8ab14b1ab3c0ec4ac8997413c9/narwhals-2.26.0.tar.gz", hash = "sha256:6b9cadca82f375c7e4cf584fdc86ca25da54827307a9c58f94547ee6104b82dd", size = 686970, upload-time = "2026-09-08T13:32:08.964Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/b5/1b84b2c784db76d69442334bc8b8748c840f13ca53be086f4f250ad4a0bc/narwhals-2.26.0-py3-none-any.whl", hash = "sha256:29326d74f107c347fd1009bd58e38d9f7c7c5b51e6de97bc93dbc325d9038b54", size = 474034, upload-time = "2026-09-08T13:32:07.159Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "numpy" +version = "2.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/01/11703282db468b85f6f7b8c7f22d058de5970d5c7e60a3a8aaa313c3de36/numpy-2.5.3.tar.gz", hash = "sha256:df2d5874ff183595a4ba404edd04f6bd9b5505c1d7708573f6a6c17489a67563", size = 20791231, upload-time = "2026-09-06T16:27:47.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/af/aa78d1a88805456e212b65461354cd943197fb9acecc4c90fd12295123a3/numpy-2.5.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7e18c623bb5c95acb3b3328861272816ba199fb531921c5d6d0b675f1fde9e3", size = 16717410, upload-time = "2026-09-06T16:24:42.745Z" }, + { url = "https://files.pythonhosted.org/packages/62/4a/8877e629445a7176297dffcaf9c485faa96a95d81728a62521ad55bd4c0f/numpy-2.5.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b5d93cf48f687479941d12b69c873ad2cc76bbd487f0091c2200636497f34034", size = 18476479, upload-time = "2026-09-06T16:24:49.35Z" }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.3.14" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/df/4b01f10069e23c641f116c62fc31e31e8dc361a153175d81561d15c8143b/nvidia_cublas_cu12-12.8.3.14-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:3f0e05e7293598cf61933258b73e66a160c27d59c4422670bf0b79348c04be44", size = 609620630, upload-time = "2025-01-23T17:55:00.753Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.57" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/6f/3683ecf4e38931971946777d231c2df00dd5c1c4c2c914c42ad8f9f4dca6/nvidia_cuda_cupti_cu12-12.8.57-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e0b2eb847de260739bee4a3f66fac31378f4ff49538ff527a38a01a9a39f950", size = 10237547, upload-time = "2025-01-23T17:47:56.863Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.61" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/22/32029d4583f7b19cfe75c84399cbcfd23f2aaf41c66fc8db4da460104fff/nvidia_cuda_nvrtc_cu12-12.8.61-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a0fa9c2a21583105550ebd871bd76e2037205d56f33f128e69f6d2a55e0af9ed", size = 88024585, upload-time = "2025-01-23T17:50:10.722Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.57" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/f6/0e1ef31f4753a44084310ba1a7f0abaf977ccd810a604035abb43421c057/nvidia_cuda_runtime_cu12-12.8.57-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:75342e28567340b7428ce79a5d6bb6ca5ff9d07b69e7ce00d2c7b4dc23eff0be", size = 954762, upload-time = "2025-01-23T17:47:22.21Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.7.1.26" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/dc/dc825c4b1c83b538e207e34f48f86063c88deaa35d46c651c7c181364ba2/nvidia_cudnn_cu12-9.7.1.26-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:6d011159a158f3cfc47bf851aea79e31bcff60d530b70ef70474c84cac484d07", size = 726851421, upload-time = "2025-02-06T22:18:29.812Z" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.41" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/26/b53c493c38dccb1f1a42e1a21dc12cba2a77fbe36c652f7726d9ec4aba28/nvidia_cufft_cu12-11.3.3.41-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:da650080ab79fcdf7a4b06aa1b460e99860646b176a43f6208099bdc17836b6a", size = 193118795, upload-time = "2025-01-23T17:56:30.536Z" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.0.11" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/9c/1f3264d0a84c8a031487fb7f59780fc78fa6f1c97776233956780e3dc3ac/nvidia_cufile_cu12-1.13.0.11-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:483f434c541806936b98366f6d33caef5440572de8ddf38d453213729da3e7d4", size = 1197801, upload-time = "2025-01-23T17:57:07.247Z" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.55" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/fc/7be5d0082507269bb04ac07cc614c84b78749efb96e8cf4100a8a1178e98/nvidia_curand_cu12-10.3.9.55-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8387d974240c91f6a60b761b83d4b2f9b938b7e0b9617bae0f0dafe4f5c36b86", size = 63618038, upload-time = "2025-01-23T17:57:41.838Z" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.2.55" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/08/953675873a136d96bb12f93b49ba045d1107bc94d2551c52b12fa6c7dec3/nvidia_cusolver_cu12-11.7.2.55-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4d1354102f1e922cee9db51920dba9e2559877cf6ff5ad03a00d853adafb191b", size = 260373342, upload-time = "2025-01-23T17:58:56.406Z" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.7.53" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/ab/31e8149c66213b846c082a3b41b1365b831f41191f9f40c6ddbc8a7d550e/nvidia_cusparse_cu12-12.5.7.53-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c1b61eb8c85257ea07e9354606b26397612627fdcd327bfd91ccf6155e7c86d", size = 292064180, upload-time = "2025-01-23T18:00:23.233Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/9a/72ef35b399b0e183bc2e8f6f558036922d453c4d8237dab26c666a04244b/nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46", size = 156785796, upload-time = "2024-10-15T21:29:17.709Z" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.26.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/ca/f42388aed0fddd64ade7493dbba36e1f534d4e6fdbdd355c6a90030ae028/nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6", size = 201319755, upload-time = "2025-03-13T00:29:55.296Z" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.61" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/f8/9d85593582bd99b8d7c65634d2304780aefade049b2b94d96e44084be90b/nvidia_nvjitlink_cu12-12.8.61-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:45fd79f2ae20bd67e8bc411055939049873bfd8fac70ff13bd4865e0b9bdab17", size = 39243473, upload-time = "2025-01-23T18:03:03.509Z" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.55" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/cd/0e8c51b2ae3a58f054f2e7fe91b82d201abfb30167f2431e9bd92d532f42/nvidia_nvtx_cu12-12.8.55-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2dd0780f1a55c21d8e06a743de5bd95653de630decfff40621dbde78cc307102", size = 89896, upload-time = "2025-01-23T17:50:44.487Z" }, +] + +[[package]] +name = "omegaconf" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "antlr4-python3-runtime", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyyaml", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/3d/e4b57b8d9008c6ebe0d5eff901f91d5700cf7bdb8c8863df817463a7fd5e/omegaconf-2.3.1.tar.gz", hash = "sha256:e5e7de64aeebeddaf8e6d3f7a783b32ac2a01c0fbd9c878012caecb891a1f42a", size = 3298472, upload-time = "2026-06-11T05:05:12.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/0e/152509871bf30df6fc38569f52a2db9b55dd41aae957adae50a053ac7778/omegaconf-2.3.1-py3-none-any.whl", hash = "sha256:3d701d14e9a8828f1edd28bb70b725908b34277cdd72cf7d6a83f94dadc6b6a0", size = 79502, upload-time = "2026-06-11T05:05:09.954Z" }, +] + +[[package]] +name = "orjson" +version = "3.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/f3/742fb1f62b825f2c010697eaf4e828004bc2a81e7e806666989c132c7c42/orjson-3.12.0.tar.gz", hash = "sha256:d14203fb1aae2ad9b3d52f8a0e82aeb10197ef1c9bc61da7f358bd70b00123d5", size = 4142915, upload-time = "2026-08-14T16:13:30.607Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/57/80b986ebfecd9c6a177ddf1c2319717f0cd8feffb2b78946595a18a2fc88/orjson-3.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1192a7021b6d071aaf909864f6e924d6a2675ca360485b972b8401749311750b", size = 131245, upload-time = "2026-08-14T16:12:35.713Z" }, + { url = "https://files.pythonhosted.org/packages/71/93/4d71f2df314a97ff0d27a4559bf5888fc8406e3c6dec90e92291e3511215/orjson-3.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:532ff8cd4bd59a327a953a7dcde922c7fc25b85e29721bb8633265430d3a3873", size = 127693, upload-time = "2026-08-14T16:12:38.627Z" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pillow" +version = "12.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" }, + { url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.11.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/18/f3bb8ef0d3b930692343da8aa4d3cbcd6749477c053959395ac81965a6e9/platformdirs-4.11.8.tar.gz", hash = "sha256:f23abafea7dd4276d1f29104b83598d7dcc567cafd07c9c951e66665645437fc", size = 37182, upload-time = "2026-09-08T22:20:42.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/e1/5b7b8bbb55084d1425bcb9bc823ff519e1b2be05f6ebb0089e2eacc38413/platformdirs-4.11.8-py3-none-any.whl", hash = "sha256:52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081", size = 24027, upload-time = "2026-09-08T22:20:41.537Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +] + +[[package]] +name = "protobuf" +version = "7.36.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/73/f66c748df06e7fe24e658eddd600d19c4b40bad836c97ce2d0ad9851fb6b/protobuf-7.36.1.tar.gz", hash = "sha256:d0f6470f0ce2b84e3feaea2d4b816378b37ba4d4aa08a274305373de93e2d524", size = 512499, upload-time = "2026-08-31T22:40:04.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/df/c799fe7a05ef16ba853a59db01f3a2c5f7d0676469589ccc4874f76a2a88/protobuf-7.36.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:97198b77e369a0abd8e262b8f6c7266c55ddb796a3a12c76d7b8881188ed83aa", size = 343228, upload-time = "2026-08-31T22:40:00.179Z" }, + { url = "https://files.pythonhosted.org/packages/39/ca/c47f91d3cab175b01fd8c4f0d80fdf8613be876cc616e66ad281a59c5ddf/protobuf-7.36.1-py3-none-any.whl", hash = "sha256:7d951e46b3f963d6c264c367c437921de9d5aedd9c3f9612b9077736b4e3ad5c", size = 179813, upload-time = "2026-08-31T22:40:03.54Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, +] + +[[package]] +name = "pycollada" +version = "0.9.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "python-dateutil", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/8d/52a5364a17eb96129962cae8d3ee7658775e085ad0ba38388684ad5944e9/pycollada-0.9.3.tar.gz", hash = "sha256:c34d6dcf0fe2eba5896f71c96d37a1c0fe1a61f08440fa0cfcec3dc2895d3302", size = 110826, upload-time = "2026-01-24T15:45:23.625Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/86/f1f61b7a0701f9d1299e5293d083318019f91021a4d449f94d59dbe024e4/pycollada-0.9.3-py3-none-any.whl", hash = "sha256:636e6496f60987586db82455ea7bbd9ade775e8181c6590c83b698b6cd53a9f5", size = 129206, upload-time = "2026-01-24T15:45:22.182Z" }, +] + +[[package]] +name = "pyglet" +version = "2.1.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/af/72a0c4fc95339ec590f441a591143e04acb27b2c94845735809c1630ade6/pyglet-2.1.16.tar.gz", hash = "sha256:ca90ee05532ced8330b8a8087ac2a7b69589b7fa7fb67f85aabe24aaf4ae42c0", size = 6598246, upload-time = "2026-08-01T01:32:35.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/93/b81e304f8954876fed84c5afffe83d2c1c0ceed064d5003b87fbfe9f9076/pyglet-2.1.16-py3-none-any.whl", hash = "sha256:27f9cabf97a64b15d335cf70d9e8979bbab5f84a9900a058206bcd3a2de341ae", size = 1037857, upload-time = "2026-08-01T01:32:30.566Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pyopengl" +version = "3.1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/16/912b7225d56284859cd9a672827f18be43f8012f8b7b932bc4bd959a298e/pyopengl-3.1.10.tar.gz", hash = "sha256:c4a02d6866b54eb119c8e9b3fb04fa835a95ab802dd96607ab4cdb0012df8335", size = 1915580, upload-time = "2025-08-18T02:33:01.76Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/e4/1ba6f44e491c4eece978685230dde56b14d51a0365bc1b774ddaa94d14cd/pyopengl-3.1.10-py3-none-any.whl", hash = "sha256:794a943daced39300879e4e47bd94525280685f42dbb5a998d336cfff151d74f", size = 3194996, upload-time = "2025-08-18T02:32:59.902Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "pyrender" +version = "0.1.45" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "freetype-py", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "imageio", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "networkx", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pillow", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyglet", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyopengl", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "scipy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "six", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "trimesh", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/5a/2a3e5bfd83071a81e02291288391e0fa2c85d1c6765357f4de2dbc27bca6/pyrender-0.1.45.tar.gz", hash = "sha256:284b2432bf6832f05c5216c4b979ceb514ea78163bf53b8ce2bdf0069cb3b92e", size = 1202386, upload-time = "2021-02-18T18:56:28.82Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/88/174c28b9d3d03cf6d8edb6f637458f30f1cf1a2bd7a617cbd9dadb1740f6/pyrender-0.1.45-py3-none-any.whl", hash = "sha256:5cf751d1f21fba4640e830cef3a0b5a95ed0f05677bf92c6b8330056b4023aeb", size = 1214061, upload-time = "2021-02-18T18:56:27.275Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "iniconfig", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pluggy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pygments", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-fcl" +version = "0.7.0.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cython", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5d/f13c1c8eed4ce3b9e1f59c6dd31850540e575390cd85917a74afc027a3c0/python_fcl-0.7.0.11-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ac7b02f47d7fb881e5786be8328da3c586af9b8c2f0eb07c4a3ed9342f810e2", size = 4659289, upload-time = "2026-04-08T05:10:27.815Z" }, +] + +[[package]] +name = "pyvers" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/62/21/9daf8da2793d112a04b46ac33ab9046c91b0af8292cd93b3cfdd07ff7169/pyvers-0.2.3.tar.gz", hash = "sha256:c4b81c3a033963245e124cdecb052783c9c4cea3bb08c051833af1c44faa6283", size = 12347, upload-time = "2026-07-09T13:58:07.046Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/8d/63787e8feda59d981ebc953b6581ccab3c97cf7bfe0a6a407c07b50fc86c/pyvers-0.2.3-py3-none-any.whl", hash = "sha256:6f5b5612f2f4bd08caa49baa70fc5f875fc7da701a5385c13e244eea6b8114dd", size = 11757, upload-time = "2026-07-09T13:58:06.078Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, +] + +[[package]] +name = "regex" +version = "2026.9.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/5c/f403115361de25809e8f785686ec7096e30fef73be9ae35aa51da4e80abb/regex-2026.9.10.tar.gz", hash = "sha256:1e321e2c84f0e52c457f5ea5944f796d6e8e09cb99738ea98dcc1bfe402a128d", size = 417072, upload-time = "2026-09-09T21:00:21.521Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/f5/dcdf5e0d898024005cfcce631e3e934d111dfbe177ca0b7f253ae8a735a2/regex-2026.9.10-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e67f8843f0e4b931f1fa860bf3bbe4134b714c0155cc5c7c0d7ea450230aae0", size = 804587, upload-time = "2026-09-09T20:57:19.859Z" }, + { url = "https://files.pythonhosted.org/packages/d0/f1/e8d7656ff3d3bd32e881d32f540b4981c79dee61908d6b790a45966e6895/regex-2026.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2479171edccced52ef02b899558f88ab2c235fe05b93180fdcae1670aacd89e1", size = 791648, upload-time = "2026-09-09T20:57:29.786Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "charset-normalizer", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "idna", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "urllib3", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "rich" +version = "14.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pygments", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/67/cae617f1351490c25a4b8ac3b8b63a4dda609295d8222bad12242dfdc629/rich-14.3.4.tar.gz", hash = "sha256:817e02727f2b25b40ef56f5aa2217f400c8489f79ca8f46ea2b70dd5e14558a9", size = 230524, upload-time = "2026-04-11T02:57:45.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/76/6d163cfac87b632216f71879e6b2cf17163f773ff59c00b5ff4900a80fa3/rich-14.3.4-py3-none-any.whl", hash = "sha256:07e7adb4690f68864777b1450859253bed81a99a31ac321ac1817b2313558952", size = 310480, upload-time = "2026-04-11T02:57:47.484Z" }, +] + +[[package]] +name = "rtree" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/09/7302695875a019514de9a5dd17b8320e7a19d6e7bc8f85dcfb79a4ce2da3/rtree-1.4.1.tar.gz", hash = "sha256:c6b1b3550881e57ebe530cc6cffefc87cd9bf49c30b37b894065a9f810875e46", size = 52425, upload-time = "2025-08-13T19:32:01.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/75/e5d44be90525cd28503e7f836d077ae6663ec0687a13ba7810b4114b3668/rtree-1.4.1-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12de4578f1b3381a93a655846900be4e3d5f4cd5e306b8b00aa77c1121dc7e8c", size = 507644, upload-time = "2025-08-13T19:31:55.164Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a4/c2292b95246b9165cc43a0c3757e80995d58bc9b43da5cb47ad6e3535213/rtree-1.4.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f155bc8d6bac9dcd383481dee8c130947a4866db1d16cb6dff442329a038a0dc", size = 1555140, upload-time = "2025-08-13T19:31:58.031Z" }, +] + +[[package]] +name = "safetensors" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/06/f955dbbb1859e3bd23c8ac6141af5106e7ad5fedec4a3a6e3d60f94b7001/safetensors-0.8.0.tar.gz", hash = "sha256:fabaf3e0f18a6618d9b36560682562157f77c2b71fcffc7b432be2baed9d753d", size = 325846, upload-time = "2026-06-09T07:52:25.563Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/50/f203ff3a3ddfe19308efc83c5a3a29ed02bf786732ec35e68bf9162f3365/safetensors-0.8.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd6f3f93c9a0a7cc2788ee63fb763353d4bd2e89b0751bc78fcf7dda00bea774", size = 516040, upload-time = "2026-06-09T07:52:16.29Z" }, + { url = "https://files.pythonhosted.org/packages/27/43/41c1621732edd934d868a00d1b891584c892a7b62a9aab82ea5a0a5623ee/safetensors-0.8.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8e080062fcde23be189565e1c3305d16751a218ecf9412c8601e64204eb6f846", size = 722361, upload-time = "2026-06-09T07:52:23.924Z" }, +] + +[[package]] +name = "scene-synthesizer" +version = "1.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "config-path", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "matplotlib", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "networkx", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "python-fcl", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyyaml", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "rtree", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools-scm", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "shapely", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "triangle", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "trimesh", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "yourdfpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/cb/ccbf1bd9c303cc10153c06fed0dd7455052b146c507018178004c34de747/scene_synthesizer-1.15.0-py3-none-any.whl", hash = "sha256:1323006ff273c4a6d9bd119b7a2cb6ecfe6535c8d576c253d867a9c55cafa247", size = 183496, upload-time = "2025-06-28T01:19:22.322Z" }, +] + +[package.optional-dependencies] +recommend = [ + { name = "dm-control", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "usd-core", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] + +[[package]] +name = "scikit-learn" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "narwhals", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "scipy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "threadpoolctl", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/eb/eaf5e07fcc0da7149b0e084f24e54edd7441b9a89ce7e034032ae97fe3a0/scikit_learn-1.9.1.tar.gz", hash = "sha256:629cada3e33e2b9bf376cdc7614a47a4140b8aedc1d836579e359736fbd82977", size = 7786908, upload-time = "2026-09-10T18:34:04.679Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/57/a50162f3d29feb979ab6347c6debda506dfb525bcff3c50dd17606651c7e/scikit_learn-1.9.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e5d7b18a5b9dca241a74695f3275fa4c895a9dadc72b3d8df5fa9d1083c9b83e", size = 9166043, upload-time = "2026-09-10T18:32:49.343Z" }, +] + +[[package]] +name = "scipy" +version = "1.18.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/74/66de6258867beb2ef08f35f9f2ac017a52cacd5081714d239ff1a442d458/scipy-1.18.1.tar.gz", hash = "sha256:52c4b7422442aba924d03ad4019852b08a92e64ea187b933135687bfe2747307", size = 30781235, upload-time = "2026-08-21T23:28:50.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/64/ff35eb9e54894cf471ff4716abd3c81eb0a0626869217ce3e6ba4ccf17d7/scipy-1.18.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f55fa87b6c612ecd6b058f167c53231b1d14e412efe361d3d6e38b3631c73218", size = 35344199, upload-time = "2026-08-21T23:24:07.844Z" }, + { url = "https://files.pythonhosted.org/packages/91/4c/075e4f66471bac101141ac739e9e135549be1bae584571bd03a530c056e1/scipy-1.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2924a03db38dc2e848bca2fe9f077dafb891480b91a00a0963a8cf86dfc31c1", size = 37480330, upload-time = "2026-08-21T23:24:19.608Z" }, +] + +[[package]] +name = "setuptools" +version = "77.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/ed/7101d53811fd359333583330ff976e5177c5e871ca8b909d1d6c30553aa3/setuptools-77.0.3.tar.gz", hash = "sha256:583b361c8da8de57403743e756609670de6fb2345920e36dc5c2d914c319c945", size = 1367236, upload-time = "2025-03-20T14:38:08.777Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/07/99f2cefae815c66eb23148f15d79ec055429c38fa8986edcc712ab5f3223/setuptools-77.0.3-py3-none-any.whl", hash = "sha256:67122e78221da5cf550ddd04cf8742c8fe12094483749a792d56cd669d6cf58c", size = 1255678, upload-time = "2025-03-20T14:38:06.621Z" }, +] + +[[package]] +name = "setuptools-scm" +version = "10.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "vcs-versioning", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/87/1ff7adcf6e03021ae58ce401565be3c9b8c739a44d14a8253d38b3754117/setuptools_scm-10.2.3.tar.gz", hash = "sha256:f179ed3e2a63cf5823e5ee9aa9ca08386219400443a0553224d72dde3f206b44", size = 67696, upload-time = "2026-09-03T13:03:29.565Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/00/e9c0591b77186f6c1a8479500456ee72cd30730836c58b7a8a2654909c23/setuptools_scm-10.2.3-py3-none-any.whl", hash = "sha256:7fcf8ff55c95eb8b572a23c81495e5559b9b90f3e82fcf0952bb76c37de27a81", size = 29123, upload-time = "2026-09-03T13:03:28.346Z" }, +] + +[[package]] +name = "shapely" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9", size = 315489, upload-time = "2025-09-24T13:51:41.432Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/37/e781683abac55dde9771e086b790e554811a71ed0b2b8a1e789b7430dd44/shapely-2.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e7d4d7ad262a48bb44277ca12c7c78cb1b0f56b32c10734ec9a1d30c0b0c54b", size = 3099844, upload-time = "2025-09-24T13:50:35.459Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a0/704c7292f7014c7e74ec84eddb7b109e1fbae74a16deae9c1504b1d15565/shapely-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:980c777c612514c0cf99bc8a9de6d286f5e186dcaf9091252fcd444e5638193d", size = 4152714, upload-time = "2025-09-24T13:50:39.9Z" }, +] + +[[package]] +name = "sharedarray" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/d2/6818d35a7abba9b2410813f2160630e125b12a52ca11acfd1fb0959433ad/SharedArray-3.2.4.tar.gz", hash = "sha256:b8b8d189110c023b9de502f9396ff2591f660fb2c9637eb13fcaf233127e50be", size = 19584, upload-time = "2024-07-18T10:10:53.084Z" } + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "tensorboard" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "grpcio", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "markdown", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pillow", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "protobuf", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tensorboard-data-server", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "werkzeug", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/4b/cd2eec9642781a8f5b2fb9994e3933a7b259ab18e9d49aeede9b5acf6311/tensorboard-2.21.0-py3-none-any.whl", hash = "sha256:7279316dcb6bd5bc391d623dea841531299cde1887310e8133bc34a996d32255", size = 5516204, upload-time = "2026-06-29T20:48:04.472Z" }, +] + +[[package]] +name = "tensorboard-data-server" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/13/e503968fefabd4c6b2650af21e110aa8466fe21432cd7c43a84577a89438/tensorboard_data_server-0.7.2-py3-none-any.whl", hash = "sha256:7e0610d205889588983836ec05dc098e80f97b7e7bbff7e994ebb78f578d0ddb", size = 2356, upload-time = "2023-10-23T21:23:32.16Z" }, + { url = "https://files.pythonhosted.org/packages/73/c6/825dab04195756cf8ff2e12698f22513b3db2f64925bdd41671bfb33aaa5/tensorboard_data_server-0.7.2-py3-none-manylinux_2_31_x86_64.whl", hash = "sha256:ef687163c24185ae9754ed5650eb5bc4d84ff257aabdc33f0cc6f74d8ba54530", size = 6590363, upload-time = "2023-10-23T21:23:35.583Z" }, +] + +[[package]] +name = "tensorboardx" +version = "2.6.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "protobuf", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/a9/fc520ea91ab1f3ba51cbf3fe24f2b6364ed3b49046969e0868d46d6da372/tensorboardx-2.6.5.tar.gz", hash = "sha256:ca176db3997ee8c07d2eb77381225956a3fd1c10c91beafab1f17069adc47017", size = 4770195, upload-time = "2026-04-03T15:40:23.803Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/0f/69fbab4c30b2f3a76e6de67585ea72a8eccf381751f5c0046b966fde9658/tensorboardx-2.6.5-py3-none-any.whl", hash = "sha256:c10b891d00af306537cb8b58a039b2ba41571f0da06f433a41c4ca8d6abe1373", size = 87510, upload-time = "2026-04-03T15:40:22.111Z" }, +] + +[[package]] +name = "tensordict" +version = "0.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cloudpickle", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "importlib-metadata", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "orjson", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyvers", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/e6/a58bfc3290322b92805f544e5cad860a9b68245752313a3c2908a847417f/tensordict-0.14.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0a748755bfb35db40123bcda67bfebac0138283d25deda76fe30be58ce10c247", size = 595446, upload-time = "2026-09-09T19:58:00.61Z" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, +] + +[[package]] +name = "timm" +version = "1.0.29" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyyaml", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "safetensors", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torchvision", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/72/58b8363e22508418d5fac897ac68e2c33f01f84d9d471edc38cbb6523dc6/timm-1.0.29.tar.gz", hash = "sha256:1af8d12bb7e15c5f96e98d60b7b8319cd7f31730778bf1af58e912a7ce171576", size = 2497559, upload-time = "2026-08-28T15:07:31.007Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/b7/19267ec60740ef1899d265272bff3623f19fe7f7bf1022fb098766db2134/timm-1.0.29-py3-none-any.whl", hash = "sha256:4266486e355c9beb9432e689de580d09e2efe7fa92a9a79e76c03e7763eac724", size = 2633555, upload-time = "2026-08-28T15:07:29.348Z" }, +] + +[[package]] +name = "tokenizers" +version = "0.23.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/1e/bc6587c5ab643b2e17776cace9070a2ae73549c86bffac9934a600bf3c31/tokenizers-0.23.2.tar.gz", hash = "sha256:7f0f085686b9de0d0079e6f874ae053600db64c5d13049e0bbc0119926d25aac", size = 385745, upload-time = "2026-09-03T08:55:42.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/ca/ca6b93c7820df123b2662a9469e8facc826ccc94e98fdd0d615f6431e73a/tokenizers-0.23.2-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41c2f84d172449b4dadb9cdc508e3e364076613c35b16e76ecfe47a60d1e3305", size = 3386843, upload-time = "2026-09-03T08:55:26.584Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d8/8e9e4e0b287a338d8f88976729628c9d22e8a54cfaf9777018a7f7cb58a0/tokenizers-0.23.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5c56bda1511921587789163e524d196ed8284174ac23abd7685d5ea8da6c4718", size = 10256304, upload-time = "2026-09-03T08:55:40.977Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "torch" +version = "2.7.1+cu128" +source = { registry = "https://download.pytorch.org/whl/cu128" } +dependencies = [ + { name = "filelock", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "fsspec", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "jinja2", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "networkx", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "sympy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.7.1%2Bcu128-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0b64f7d0a6f2a739ed052ba959f7b67c677028c9566ce51997f9f90fe573ddaa", upload-time = "2025-06-03T18:31:26Z" }, +] + +[[package]] +name = "torch-geometric" +version = "2.8.0.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "fsspec", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "jinja2", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "psutil", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyparsing", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "requests", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tqdm", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "xxhash", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/2c/bfcb404c448f7c347cea7b956d3c7e92d53e36c8db2010eb1bdc1b937bfb/torch_geometric-2.8.0.post1.tar.gz", hash = "sha256:2c0c81666ec10f2132f6b4e0b6c57fc813f2c9f6b57599d7b7a799c614c22fbd", size = 928666, upload-time = "2026-07-20T20:44:40.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/5c/edf74a71249ad19aa0390fb97ff021e2a5b04ce23252730014e1feac957e/torch_geometric-2.8.0.post1-py3-none-any.whl", hash = "sha256:5d9841cbfa64eadc425e445767127d103dd52fdc5890548c6364986c9bc78029", size = 1325397, upload-time = "2026-07-20T20:44:38.789Z" }, +] + +[[package]] +name = "torchvision" +version = "0.22.1+cu128" +source = { registry = "https://download.pytorch.org/whl/cu128" } +dependencies = [ + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pillow", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.22.1%2Bcu128-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f64ef9bb91d71ab35d8384912a19f7419e35928685bc67544d58f45148334373", upload-time = "2025-06-03T18:37:28Z" }, +] + +[[package]] +name = "tqdm" +version = "4.70.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/ea/b2a5bd54b28a324dae8211928b2d730b6547500342c7e6c6dea08bd0a485/tqdm-4.70.1.tar.gz", hash = "sha256:cefd0eca11b2a37a3aee776544d4f4ae913f02688135b5556b8788dfa474afc4", size = 171846, upload-time = "2026-09-11T07:25:16.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl", hash = "sha256:c293e525e6fef9c20e8728fd4612df02a0aa31bb5fe91ecd93e123b1b7bffa73", size = 80199, upload-time = "2026-09-11T07:25:14.599Z" }, +] + +[[package]] +name = "transformers" +version = "5.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyyaml", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "regex", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "safetensors", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tokenizers", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tqdm", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typer", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/9e/750649904a065007a838981785b2bd8d9ff26154c6c341ac67d0b7f82c68/transformers-5.17.0.tar.gz", hash = "sha256:a153be279169b55b92d8000bf4af294aed684503d091cca7804da2dd8a9de000", size = 9817878, upload-time = "2026-09-09T15:39:56.886Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/d0/c502b60d684adbd98a8dc7d5bb866842772b816ac4354e4608be240041ae/transformers-5.17.0-py3-none-any.whl", hash = "sha256:78ec1ce21579b38dfb83950a0658cd119f87212a2fcfdff478096ce9d6c03801", size = 12295140, upload-time = "2026-09-09T15:39:53.746Z" }, +] + +[[package]] +name = "triangle" +version = "20250106" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/93/ce4d0c46ff570993f4302ce55300dd310b7c957a8e66890ed00691229f5b/triangle-20250106-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38c221bb6e403f81de49050899502a8326c9565f62c4d69171070b41dc25cb69", size = 2089547, upload-time = "2025-01-07T04:56:09.463Z" }, +] + +[[package]] +name = "trimesh" +version = "5.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/3f/a13d797349fdd00a8586b60d6b71df849e1766e2002729697a265b27a773/trimesh-5.1.0.tar.gz", hash = "sha256:927aa8748af8b84cb969c8f53270d929e546f2beada77a2bc234b605fddf1454", size = 870501, upload-time = "2026-08-31T18:28:55.464Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/c8/5ede3b786ef58e0076e9ca748b2c1787183f85e5ad8133373715c31248f9/trimesh-5.1.0-py3-none-any.whl", hash = "sha256:0fa85d1d131e321b683c00747c20090200b92071298b905ea588f609eb204c89", size = 750004, upload-time = "2026-08-31T18:28:53.746Z" }, +] + +[[package]] +name = "triton" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/5f/950fb373bf9c01ad4eb5a8cd5eaf32cdf9e238c02f9293557a2129b9c4ac/triton-3.3.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9999e83aba21e1a78c1f36f21bce621b77bcaa530277a50484a7cb4a822f6e43", size = 155669138, upload-time = "2025-05-29T23:39:51.771Z" }, +] + +[[package]] +name = "typer" +version = "0.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "rich", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "shellingham", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/f7/57713ba479fd405eb76de31404b2c744c289e336b2d999511ebf51e496f7/typer-0.27.2.tar.gz", hash = "sha256:269b7eb9d3c202ca84b4bc9618cb04ebb43d3d4d1e567e4c768607232c05f945", size = 204045, upload-time = "2026-08-28T10:26:55.046Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/bf/205d0004930ede8f542fb58f601526fccf4ae7626075ca1e6c4de5d3d652/typer-0.27.2-py3-none-any.whl", hash = "sha256:b3a5fc4342d5fc8fda8fc3010b1cf117e9249aab7fae800c2eff62fd3842d97d", size = 123130, upload-time = "2026-08-28T10:26:53.752Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "urdfpy" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lxml", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "networkx", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pillow", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pycollada", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyrender", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "six", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "trimesh", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/8d/3e64966a55afe0214f2f006730dad7c615828117b0c56de0b8819c6575bc/urdfpy-0.0.4.tar.gz", hash = "sha256:5bae6e06572b72426565bcca2106de712a18cfb91a6b00c159451263e72a5839", size = 20777, upload-time = "2019-03-03T23:23:04.779Z" } + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "usd-core" +version = "26.8" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/0b/efe5c0d7c273149ac94083831e5b36649925a621a73cd2ddf9616e160c8c/usd_core-26.8-cp312-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:204fb1ef1fc977b827c491f4e9fa39b57518504ed0cacfe12e026e95123cacf1", size = 29530957, upload-time = "2026-07-20T14:32:24.255Z" }, +] + +[[package]] +name = "vcs-versioning" +version = "2.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a0/00/c883501b0c55b7d0ec33e67813e9a38563ea84bfecebce9b3347fa52d845/vcs_versioning-2.3.4.tar.gz", hash = "sha256:f3443da15a34a32755b04324e33c6166add300c7661f45902225190c9337f711", size = 149647, upload-time = "2026-09-03T13:04:31.205Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/30/cbf2883ee604dc83ff510516ae05e93b17bfcb4d18279ae12905e13f49da/vcs_versioning-2.3.4-py3-none-any.whl", hash = "sha256:076afa0e12a42a23faba92bcb62cb016c1ce38051ceee1f302de1e2676a68bbc", size = 115414, upload-time = "2026-09-03T13:04:29.925Z" }, +] + +[[package]] +name = "viser" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "imageio", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "msgspec", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "requests", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "rich", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tqdm", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "trimesh", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "websockets", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "zstandard", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/4d/e3d9bb3cb71a3f5de21b9f35fb4db09c6f873885c02e9d9ace20d221aae1/viser-1.1.0.tar.gz", hash = "sha256:28c27da73f391fdc7a1ffc3e3d122fd8eede9d15508128bc53c7e02904169412", size = 5310056, upload-time = "2026-08-16T21:15:10.628Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/b1/37b6fefb2f2fe5f11ae305d0a428e1cdb4f5868112c42ab29c1ce1629f06/viser-1.1.0-py3-none-any.whl", hash = "sha256:d683accd18d457d3971e107d6269b5a9e2dcc93d2a8661544ac433542e4550e8", size = 5464160, upload-time = "2026-08-16T21:15:08.786Z" }, +] + +[[package]] +name = "webdataset" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "braceexpand", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pyyaml", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/3a/68800d92e065cf4750ebecf973b13979c0c929b439e1293012938862038d/webdataset-1.0.2.tar.gz", hash = "sha256:7f0498be827cfa46cc5430a58768a24e2c6a410676a61be1838f53d61afdaab4", size = 80090, upload-time = "2025-06-19T23:26:21.945Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/00/aca6beb3658dab4ed3dbb41a78e6e7f31342e0b41d28088f205525751601/webdataset-1.0.2-py3-none-any.whl", hash = "sha256:3dbfced32b25c0d199c6b9787937b6f85742bc3c84f652c846893075c1c082d9", size = 74956, upload-time = "2025-06-19T23:26:20.354Z" }, +] + +[[package]] +name = "websockets" +version = "16.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/f7/bc3a25c5ec26ce62ce487690becc2f3710bbc7b33338f005ad390db0b986/websockets-16.1.1.tar.gz", hash = "sha256:db234eda965dcce15df96bb9709f587cd87d4d52aaf0e80e2f34ec04c7670c57", size = 182204, upload-time = "2026-07-17T22:51:05.858Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ed/f1831681fce0e3242346e5458486003c5f124ed69e5e0b847fd029db4973/websockets-16.1.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f62863e8a00a6d33c3d6566ec0b89f23787b747ffe0c3bc71ec0e76b82c94b1", size = 187137, upload-time = "2026-07-17T22:49:18.323Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0b/f78de76ff446f1e66af12b43c48a35f31744de93cfdec2f4ea67d5d7bbf1/websockets-16.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:53260c8930da5771cec89439bff99c20c8cb03ddb9588b980697355a83cd4bd3", size = 187102, upload-time = "2026-07-17T22:49:34.616Z" }, + { url = "https://files.pythonhosted.org/packages/be/4d/2d0d67834092e354d2b0498f014a41249a89556bc406cf86f3e1557bb463/websockets-16.1.1-py3-none-any.whl", hash = "sha256:6abbd3e82c731c8e531714466acd5d87b5e88ac3243465337ba71d68e23ae7e3", size = 173814, upload-time = "2026-07-17T22:51:04.184Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" }, +] + +[[package]] +name = "wrapt" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/42/a6/6375d56c44d590ef24acf0f8f5bf7ed768ff7a510b959306ec412611e90f/wrapt-2.4.1.tar.gz", hash = "sha256:fd6390aab9e8aa40c52eff3c180f098e8d9f5894b1fd4c4fd2c207067b33ed16", size = 164597, upload-time = "2026-09-10T23:12:16.811Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/4f/17a89a580cb0082e61b8375074d5c9e5d38e4aa83ee19b6174ee472d17c2/wrapt-2.4.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:30d11c289b013bf384ff1a1a6553f150d0b855901708a9bef667a5680f8247c9", size = 236301, upload-time = "2026-09-10T23:10:11.039Z" }, + { url = "https://files.pythonhosted.org/packages/79/10/248841cb30107f6f32c53a02662e1c3e0c7c06bea0b8ebfaaee94885dcee/wrapt-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edd03758a7578526642508b8833d43496fdfba0f64e0025dfca153a7c1777735", size = 225103, upload-time = "2026-09-10T23:10:18.346Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a2/edcfc8d9a30375791b775715f8501364588f65b494ec4f6930568a19e765/wrapt-2.4.1-py3-none-any.whl", hash = "sha256:1e84ec5d89a0a07a0ef6bcd343f5c8ecdc95601d71de3058cdc63274e86c193c", size = 75317, upload-time = "2026-09-10T23:12:14.82Z" }, +] + +[[package]] +name = "xxhash" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/a5/1386f35da1475fcaeef42581deae73417c6d2a6a0b2d2e8914de18844dcd/xxhash-4.0.1.tar.gz", hash = "sha256:d55bf4ef10eb09b8b6866790e083d26d087d84caa3cc0946ba87c3ca7ecaf7b7", size = 101513, upload-time = "2026-08-17T08:24:08.557Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/97/31bd8b8279e6935a0719f6910ced15e9d5a2cd554b253f6027ce1b5a1c2c/xxhash-4.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:237b8f63a2a0fcfb1ffc06e21dad23add44e6d354b2b014364a1d41e419a4dee", size = 261812, upload-time = "2026-08-17T08:22:00.469Z" }, + { url = "https://files.pythonhosted.org/packages/6d/32/c6148d39a49efa95f39b4cf0d41ef35a487f3b30f6fb1fc8fe8d8eab577e/xxhash-4.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96d8de55029d42251945531f6aa7590c32b48163c66a43bf29d8657d7446a377", size = 258174, upload-time = "2026-08-17T08:35:21.18Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fb/0b04b68d6c5bc71c7a2c344f1287327b67e607f28fbcfd937697caca64b6/xxhash-4.0.1-cp312-cp312-pyemscripten_2024_0_wasm32.whl", hash = "sha256:0163b5d259de23ae9e07b7eabf435ce4704f6f205589a2b154e6af4be985ce1b", size = 20767, upload-time = "2026-08-17T08:21:00.806Z" }, + { url = "https://files.pythonhosted.org/packages/ad/23/2d549e5d5d7759eaf9ac2d2d2ab81ff60f1bb2b52cdaae8e5ec5c6524354/xxhash-4.0.1-graalpy312-graalpy250_312_native-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deca2a30d983d240b8375ec2ee0a4288e72042827fc61df2f7671f8467e4cb2f", size = 38206, upload-time = "2026-08-17T08:36:32.193Z" }, +] + +[[package]] +name = "yapf" +version = "0.40.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "platformdirs", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "tomli", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/7a/9020bfa17d294b5d0d8bf26bb175ad4c90d1e3ad4039001f621ef046cb06/yapf-0.40.1.tar.gz", hash = "sha256:958587eb5c8ec6c860119a9c25d02addf30a44f75aa152a4220d30e56a98037c", size = 247509, upload-time = "2023-06-20T05:43:26.352Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/75/c374517c09e31bf22d3b3f156d73e0f38d08e29b2afdd607cef5f1e10aa9/yapf-0.40.1-py3-none-any.whl", hash = "sha256:b8bfc1f280949153e795181768ca14ef43d7312629a06c43e7abd279323af313", size = 250316, upload-time = "2023-06-20T05:43:23.68Z" }, +] + +[[package]] +name = "yarl" +version = "1.24.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "multidict", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "propcache", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/31/33/ebe9e3d1f86c7a0b51094c0a146392045ca1631d2664889539dec8088a33/yarl-1.24.5.tar.gz", hash = "sha256:e81b83143bee16329c23db3c1b2d82b29892fcbcb849186d2f6e98a5abe9a57f", size = 228679, upload-time = "2026-07-20T02:07:45.435Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/d0/d56c859b8222116f5d68459199f48359e0bf121b6f65a69bf329b3602ba0/yarl-1.24.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f08c7513ecef5aad65687bfdf6bc601ae9fccd04a42904501f8f7141abad9eb9", size = 109835, upload-time = "2026-07-20T02:05:39.506Z" }, + { url = "https://files.pythonhosted.org/packages/a5/43/fab2d1dad9d340a268cdde63756a123d069723efff6a372d123fa74a9517/yarl-1.24.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:65be18ec59496c13908f02a2472751d9ef840b4f3fb5726f129306bf6a2a7bba", size = 110006, upload-time = "2026-07-20T02:05:53.554Z" }, + { url = "https://files.pythonhosted.org/packages/61/02/962c1cbfc401a30c1d034dc67ff395f64b52302c6d62de556c1fca99acc0/yarl-1.24.5-py3-none-any.whl", hash = "sha256:a33700d13d9b7d84fd10947b09ff69fb9a792e519c8cb9764a3ca70baa6c23a7", size = 58612, upload-time = "2026-07-20T02:07:43.461Z" }, +] + +[[package]] +name = "yourdfpy" +version = "0.0.60" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lxml", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "six", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "trimesh", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/19/20c50861f30aff7720f9a601f386d73760c2df9961de1f98d0dbf3b85e69/yourdfpy-0.0.60.tar.gz", hash = "sha256:2af2d8bdeea1b85b642590a3b4236fdb35746d7b3e38ce460a169c18d9c4f868", size = 538238, upload-time = "2026-01-23T07:32:47.856Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/60/4ea0d6df0b497d51bf2ef87eaab0eb26f8bc3b3313c012da5df3383cced9/yourdfpy-0.0.60-py3-none-any.whl", hash = "sha256:8a187a8b18c98db87c76e9a950581b3c875b761e00df83942526c17ea693166c", size = 22194, upload-time = "2026-01-23T07:32:46.481Z" }, +] + +[[package]] +name = "zipp" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214, upload-time = "2026-05-18T20:08:57.967Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238, upload-time = "2026-05-18T20:08:57.045Z" }, +] + +[[package]] +name = "zstandard" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" }, + { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" }, +] diff --git a/dimos/manipulation/grasping/test_grasp_gen_x.py b/dimos/manipulation/grasping/grasp_gen_x/test_module.py similarity index 85% rename from dimos/manipulation/grasping/test_grasp_gen_x.py rename to dimos/manipulation/grasping/grasp_gen_x/test_module.py index 5621eeb711..7057ff84b4 100644 --- a/dimos/manipulation/grasping/test_grasp_gen_x.py +++ b/dimos/manipulation/grasping/grasp_gen_x/test_module.py @@ -25,13 +25,17 @@ import pytest from pytest_mock import MockerFixture +from dimos.experimental.isolated_python.bootstrap import validate_runtime from dimos.manipulation.grasping.grasp_gen_spec import GraspGenSpec, LegacyGraspGenSpec -import dimos.manipulation.grasping.grasp_gen_x as grasp_gen_x -from dimos.manipulation.grasping.grasp_gen_x import ( +from dimos.manipulation.grasping.grasp_gen_x.module import ( GraspGenXConfig, GraspGenXError, GraspGenXModule, ) +from dimos.manipulation.grasping.grasp_gen_x.python.graspgenx_runtime import runtime as grasp_gen_x +from dimos.manipulation.grasping.grasp_gen_x.python.graspgenx_runtime.runtime import ( + _GraspGenXRuntimeModule, +) from dimos.msgs.geometry_msgs.Pose import Pose from dimos.msgs.manipulation_msgs.GraspCandidate import GraspCandidate from dimos.msgs.manipulation_msgs.GraspCandidateArray import GraspCandidateArray @@ -69,8 +73,8 @@ def test_public_adapter_import_does_not_load_optional_runtime() -> None: "-c", ( "import sys; " - "import dimos.manipulation.grasping.grasp_gen_x; " - "assert 'dimos.manipulation.grasping.grasp_gen_x_runtime' not in sys.modules" + "import dimos.manipulation.grasping.grasp_gen_x.module; " + "assert 'graspgenx' not in sys.modules; assert 'graspgenx_runtime.backend' not in sys.modules" ), ], check=False, @@ -159,7 +163,7 @@ def test_rigid_transform_relational_validation() -> None: def test_start_is_synchronous_and_idempotent(runtime: Any) -> None: - module = GraspGenXModule(**module_args()) + module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) try: module.start() module.start() @@ -172,7 +176,7 @@ def test_start_is_synchronous_and_idempotent(runtime: Any) -> None: def test_start_failure_is_explicit(runtime: Any) -> None: runtime.side_effect = RuntimeError("CUDA unavailable") - module = GraspGenXModule(**module_args()) + module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) try: with pytest.raises(GraspGenXError, match="initialize"): module.start() @@ -196,7 +200,7 @@ def test_adapter_sorts_stably_truncates_and_applies_tcp_transform(runtime: Any) (0.0, 0.0, 0.0, 1.0), ), ) - module = GraspGenXModule(**module_args(cfg)) + module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args(cfg)) try: module.start() result = module.propose_grasps(cloud()) @@ -214,7 +218,7 @@ def test_empty_backend_result_preserves_input_header(runtime: Any) -> None: np.empty((0, 4, 4), dtype=np.float32), np.empty(0, dtype=np.float32), ) - module = GraspGenXModule(**module_args()) + module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) try: module.start() result = module.propose_grasps(cloud()) @@ -235,7 +239,7 @@ def test_empty_backend_result_preserves_input_header(runtime: Any) -> None: ], ) def test_invalid_cloud_points_are_rejected(runtime: Any, points: np.ndarray) -> None: - module = GraspGenXModule(**module_args()) + module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) try: module.start() with pytest.raises(ValueError, match="pointcloud|XYZ"): @@ -257,7 +261,7 @@ def test_invalid_backend_outputs_are_rejected( runtime: Any, backend: tuple[np.ndarray, np.ndarray] ) -> None: runtime.return_value.infer.return_value = backend - module = GraspGenXModule(**module_args()) + module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) try: module.start() with pytest.raises(ValueError): @@ -268,7 +272,7 @@ def test_invalid_backend_outputs_are_rejected( def test_inference_failure_is_wrapped(runtime: Any) -> None: runtime.return_value.infer.side_effect = RuntimeError("backend") - module = GraspGenXModule(**module_args()) + module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) try: module.start() with pytest.raises(GraspGenXError, match="inference"): @@ -278,7 +282,7 @@ def test_inference_failure_is_wrapped(runtime: Any) -> None: def test_not_started_and_missing_metadata_are_rejected(runtime: Any) -> None: - module = GraspGenXModule(**module_args()) + module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) missing_frame = cloud() missing_frame.frame_id = "" missing_timestamp = cloud() @@ -293,3 +297,10 @@ def test_not_started_and_missing_metadata_are_rejected(runtime: Any) -> None: module.propose_grasps(missing_timestamp) finally: module.stop() + + +def test_runtime_implements_the_host_contract() -> None: + validate_runtime(GraspGenXModule, _GraspGenXRuntimeModule) + assert inspect.signature(GraspGenXModule.propose_grasps) == inspect.signature( + _GraspGenXRuntimeModule.propose_grasps + ) diff --git a/dimos/robot/all_blueprints.py b/dimos/robot/all_blueprints.py index ea08606d7c..8e1dcafb1b 100644 --- a/dimos/robot/all_blueprints.py +++ b/dimos/robot/all_blueprints.py @@ -247,7 +247,7 @@ "go2-zenoh": "dimos.robot.unitree.go2.zenoh.zenohconnection.GO2Zenoh", "google-maps-skill-container": "dimos.agents.skills.google_maps_skill_container.GoogleMapsSkillContainer", "gps-nav-skill-container": "dimos.agents.skills.gps_nav_skill.GpsNavSkillContainer", - "grasp-gen-x-module": "dimos.manipulation.grasping.grasp_gen_x.GraspGenXModule", + "grasp-gen-x-module": "dimos.manipulation.grasping.grasp_gen_x.module.GraspGenXModule", "grasping-module": "dimos.manipulation.grasping.grasping.GraspingModule", "gstreamer-camera-module": "dimos.hardware.sensors.camera.gstreamer.gstreamer_camera.GstreamerCameraModule", "habitat-connection": "dimos.simulation.habitat.connection.HabitatConnection", diff --git a/dimos/robot/manipulators/xarm/blueprints/grasp.py b/dimos/robot/manipulators/xarm/blueprints/grasp.py index d46df33fc0..8e34d32a39 100644 --- a/dimos/robot/manipulators/xarm/blueprints/grasp.py +++ b/dimos/robot/manipulators/xarm/blueprints/grasp.py @@ -31,7 +31,7 @@ from dimos.core.coordination.blueprints import Blueprint, autoconnect from dimos.core.global_config import global_config from dimos.hardware.sensors.camera.realsense.camera import RealSenseCamera -from dimos.manipulation.grasping.grasp_gen_x import GraspGenXModule +from dimos.manipulation.grasping.grasp_gen_x.module import GraspGenXModule from dimos.manipulation.grasping.heuristic_grasp import HeuristicGraspModule from dimos.manipulation.manipulation_module import ManipulationModule from dimos.manipulation.manipulation_skills import ManipulationSkills diff --git a/docs/capabilities/manipulation/index.md b/docs/capabilities/manipulation/index.md index 75d2d81264..d497fded0c 100644 --- a/docs/capabilities/manipulation/index.md +++ b/docs/capabilities/manipulation/index.md @@ -316,8 +316,8 @@ Python extras do not install native RealSense binaries, vendor SDK setup, system libraries, or robot/model assets. Follow the hardware-specific setup instructions. Agentic blueprints require provider credentials; the default EdgeTAM backend requires CUDA or MPS. The bundle includes CPU ONNX inference; -specialized CUDA backends, GraspGenX, dataset export (`learning`), and DDS remain -separate extras. Linux x86_64 is the primary supported bundle platform; backend +specialized CUDA backends, dataset export (`learning`), and DDS remain +separate extras. GraspGenX prepares its own isolated Python environment on first use. Linux x86_64 is the primary supported bundle platform; backend and hardware wheel availability still limits macOS and ARM installations. Safety behavior for unsupported RoboPlan features: diff --git a/docs/capabilities/manipulation/xarm-grasp.md b/docs/capabilities/manipulation/xarm-grasp.md index b837fd0b87..efc0dcbed8 100644 --- a/docs/capabilities/manipulation/xarm-grasp.md +++ b/docs/capabilities/manipulation/xarm-grasp.md @@ -20,13 +20,30 @@ Miss `--xarm7-ip` on hardware and the arm has no address to reach; leave `xarm-grasp-agent` and `xarm-grasp-graspgenx-agent` add an MCP agent over the top; drive those with `dimos agent-send "..."`. -`xarm-grasp-graspgenx` needs the `graspgenx` extra and a CUDA GPU. Checkpoints -download once from Hugging Face and cache under `~/.cache/huggingface`. +`xarm-grasp-graspgenx` runs GraspGenX in an isolated Python 3.12 environment +on Linux x86_64 with a CUDA GPU compatible with Torch 2.7.1 / CUDA 12.8. +Install `uv >=0.9.25` on PATH. The first launch automatically prepares the separately +locked dependencies; +no GraspGenX extra is installed in the main DimOS environment. Virtual environments +live under `~/.cache/dimos/isolated-python` (or `$XDG_CACHE_HOME/dimos/isolated-python`). +Pinned checkpoints download from Hugging Face during module startup and reuse +its normal cache under `~/.cache/huggingface`. + +Source checkouts supply their current DimOS code to the runtime. Wheel installations +resolve the exact installed DimOS version from the configured package source; that +version must be available there. For a locally built wheel, make it available through +uv's `--find-links` configuration (`UV_FIND_LINKS`) before launching. + +To validate GPU proposals without moving a robot, run from a source checkout: ```bash -uv sync --extra graspgenx +uv run python -m dimos.manipulation.grasping.grasp_gen_x.demo_inference ``` +This checks the actual isolated environment, loads the model, proposes grasps for +a recorded object cloud, and verifies output and process cleanup. Missing CUDA or +failed model initialization fails the check. + What differs between the arm and the sim is decided at import time: the hardware adapter, the base pose, the camera (RealSense plus its mount edge, versus the MuJoCo wrist camera), the detector backends, and the home pose. diff --git a/pyproject.toml b/pyproject.toml index 7606ded1b3..1ee9f52708 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ include = ["dimos*"] # .py modules out of the wheel too). exclude = [ "dimos.experimental.isolated_python.example.python*", + "dimos.manipulation.grasping.grasp_gen_x.python*", "dimos.web.websocket_vis.node_modules*", "dimos.web.command-center-extension*", "dimos.web.dimos_interface.src*", @@ -50,12 +51,16 @@ exclude = [ "**/*.svg", "**/*.tcss", "**/*.pyi", + "manipulation/grasping/grasp_gen_x/python/pyproject.toml", + "manipulation/grasping/grasp_gen_x/python/uv.lock", + "manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/*.py", ] [tool.setuptools.exclude-package-data] # Trims data the recursive globs above would otherwise pull in: test fixtures # and the native (C++/nix) build tree, which is built separately. "dimos" = [ + "manipulation/grasping/grasp_gen_x/fixtures/**", "agents/fixtures/**", "agents/mcp/fixtures/**", "mapping/google_maps/fixtures/**", @@ -373,14 +378,6 @@ scene = [ "usd-core>=23.11", ] -graspgenx = [ - "graspgenx", - "huggingface-hub>=0.30,<1", - "matplotlib>=3.7.1", - "torch>=2.1,<2.7", - "torchvision>=0.16,<0.22", -] - all = [ "dimos[apriltag,cuda,drone,manipulation,misc,scene,unitree,webrtc]", ] @@ -545,22 +542,12 @@ override-dependencies = [ # uv picks importlib-metadata>=9 and cascades the otel stack down to 1.11.1, # whose stale _pb2.py files crash with protobuf>=6. "importlib-metadata<8.8.0", - # GraspGenX pins older versions that conflict with the current DimOS stack. - # These versions were validated with the pinned GraspGenX source revision. - "yourdfpy>=0.0.60", - "trimesh>=4.12", - "numpy>=2", - "timm>=1.0.17", - "huggingface-hub>=0.30,<1", - "diffusers>=0.29", - "pyopengl>=3.1.5", # Force the torch 2.7 line even if a transitive dep asks for newer. "torch>=2.1,<2.8", "torchvision>=0.16,<0.23", ] [tool.uv.sources] -graspgenx = { git = "https://github.com/NVlabs/GraspGenX.git", rev = "b9429097728cb1c430dd78b92edf17ba318aad03" } # CUDA builds are not published for macOS or non-x86_64 Linux. Those platforms # fall back to PyPI. torch = [ @@ -654,8 +641,6 @@ module = [ "etils", "faster_whisper", "geometry_msgs.*", - "graspgenx", - "graspgenx.*", "h5py", "h5py.*", "mcap", @@ -757,6 +742,7 @@ max_size_kb = 75 ignore = [ "uv.lock", "dimos/experimental/isolated_python/example/python/uv.lock", + "dimos/manipulation/grasping/grasp_gen_x/python/uv.lock", "*/package-lock.json", "Cargo.lock", "*/Cargo.lock", diff --git a/uv.lock b/uv.lock index 06d991753e..891b7b40cb 100644 --- a/uv.lock +++ b/uv.lock @@ -52,22 +52,15 @@ constraints = [ { name = "onnxruntime-gpu", marker = "python_full_version < '3.11'", specifier = "<1.24" }, ] overrides = [ - { name = "diffusers", specifier = ">=0.29" }, - { name = "huggingface-hub", specifier = ">=0.30,<1" }, { name = "importlib-metadata", specifier = "<8.8.0" }, { name = "langgraph-prebuilt", specifier = "<=1.0.8" }, - { name = "numpy", specifier = ">=2" }, { name = "opencv-python", marker = "sys_platform == 'never'" }, { name = "pillow", specifier = ">=12.2.0" }, - { name = "pyopengl", specifier = ">=3.1.5" }, { name = "pytest", specifier = "==8.3.5" }, - { name = "timm", specifier = ">=1.0.17" }, { name = "torch", marker = "(platform_machine != 'x86_64' and sys_platform == 'linux') or (sys_platform != 'linux' and sys_platform != 'win32')", specifier = ">=2.1,<2.8" }, { name = "torch", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or sys_platform == 'win32'", specifier = ">=2.1,<2.8", index = "https://download.pytorch.org/whl/cu128" }, { name = "torchvision", marker = "(platform_machine != 'x86_64' and sys_platform == 'linux') or (sys_platform != 'linux' and sys_platform != 'win32')", specifier = ">=0.16,<0.23" }, { name = "torchvision", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or sys_platform == 'win32'", specifier = ">=0.16,<0.23", index = "https://download.pytorch.org/whl/cu128" }, - { name = "trimesh", specifier = ">=4.12" }, - { name = "yourdfpy", specifier = ">=0.0.60" }, ] [[package]] @@ -664,15 +657,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a7/bb/fb78b84e5e5b1dda58625be89f3cd66aa48b5e9600689d8e0b9b3eb5667c/bosdyn_core-5.1.4-py3-none-any.whl", hash = "sha256:b4661fb162fe50e788268530c21fca91ca25e6f595ac9535722954145995b9ac", size = 32228, upload-time = "2026-03-18T13:49:36.964Z" }, ] -[[package]] -name = "braceexpand" -version = "0.1.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/93/badd4f5ccf25209f3fef2573073da9fe4a45a3da99fca2f800f942130c0f/braceexpand-0.1.7.tar.gz", hash = "sha256:e6e539bd20eaea53547472ff94f4fb5c3d3bf9d0a89388c4b56663aba765f705", size = 7777, upload-time = "2021-05-07T13:49:07.323Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/93/e8c04e80e82391a6e51f218ca49720f64236bc824e92152a2633b74cf7ab/braceexpand-0.1.7-py2.py3-none-any.whl", hash = "sha256:91332d53de7828103dcae5773fb43bc34950b0c8160e35e0f44c4427a3b85014", size = 5923, upload-time = "2021-05-07T13:49:05.146Z" }, -] - [[package]] name = "bracex" version = "3.0.1" @@ -1271,15 +1255,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, ] -[[package]] -name = "config-path" -version = "1.0.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/32/02/75ee14911378497eb3bb1bfa576cd99bc88ecf0d9f60671721a91dbecaaf/config-path-1.0.5.tar.gz", hash = "sha256:ed17ad1a0cbdadb2781443a27a624057138611416885acb3fb41132a6d559d77", size = 11215, upload-time = "2023-10-03T19:41:43.639Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/51/eec3b34b71799668009107b48fa146b9b9d347f60749491ca77a85ccb94c/config_path-1.0.5-py3-none-any.whl", hash = "sha256:7a69fbfdacb95d7591621e67265a87960d54d56abffe95d9f93acd342f926292", size = 8800, upload-time = "2023-10-03T19:41:42.129Z" }, -] - [[package]] name = "configargparse" version = "1.7.1" @@ -1611,36 +1586,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/ab/acaa119f552019bdb2b06478553cf712967672f5970be80ecc9b4ca805f4/cyclonedds-0.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:103a681e9490229f12c151a125e00c4db8fdb344c8e12e35ee515cd9d5d1ecd7", size = 1200672, upload-time = "2024-06-05T18:50:54.303Z" }, ] -[[package]] -name = "cython" -version = "3.2.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/de/db48b8870e766cfea809986cc50c1e986c663a9ab7bafd0ac1a2512c4a26/cython-3.2.9.tar.gz", hash = "sha256:d249c9022ab13286b17bd66f30609e800c5f95efeecb06168990c7a66cecde6c", size = 3293493, upload-time = "2026-07-24T06:21:21.867Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/29/bc7e088201af74eacbb5799542d0f09d6e5e139cc55f19fd54c409f58109/cython-3.2.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2751b7c0cb13135aadcc1cc8fe223be98d458cd1132611d31675e768a5e4a2e9", size = 3000461, upload-time = "2026-07-24T06:21:37.078Z" }, - { url = "https://files.pythonhosted.org/packages/81/e8/ac8266fa88b3a80009a2fa55b2729be1518c05d182888223f95b8e2a2b8e/cython-3.2.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ccb0bc6b8437cfcb04459a02bc5ecc58bf161ea11659438945ab5a1392ee39fe", size = 3306728, upload-time = "2026-07-24T06:21:39.011Z" }, - { url = "https://files.pythonhosted.org/packages/df/9b/b4ff8cdff357ba0f41791e044a2a95898d0768b20e9247823decf5eabb5e/cython-3.2.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c2974742433be5c36ae1df1e761ce0063753fc2702316d5a3062b0a4e94a1df", size = 3458732, upload-time = "2026-07-24T06:21:41.034Z" }, - { url = "https://files.pythonhosted.org/packages/f3/eb/06ba035f8b845d2d7d67f533a58ebd885ceab1e48b8cc442f093ecf59201/cython-3.2.9-cp310-cp310-win_amd64.whl", hash = "sha256:a3fc783d12202d1b064b6f125772d85f00e36e62eee6b2e415f56d8fd2d2e7b2", size = 2785625, upload-time = "2026-07-24T06:21:42.969Z" }, - { url = "https://files.pythonhosted.org/packages/fc/97/2f477d30fc6ca0ff333552233aa6dee0e57323a55913f84f24da9cac26fc/cython-3.2.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e8ec2d5a7b798c84d6779e7ca5318fd928b8fe9dd021106d714dc2e77cdc7555", size = 2992696, upload-time = "2026-07-24T06:21:44.931Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bf/0f280f7960c129957d2ce650e7fd7dec8e706c26d61a428e93927f4c9904/cython-3.2.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cac792b0bde1c86d8f832e513cd061b7e682181cc5a6d843d487e6c8ae9d5fcb", size = 3307656, upload-time = "2026-07-24T06:21:46.649Z" }, - { url = "https://files.pythonhosted.org/packages/ac/cd/6d7c35a1065f1a07c9fa5ec784ee562f32f36a8fd05748e5f10694887eb7/cython-3.2.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8c843dd85857cd0d0da055489f448d032866120cfb094ce16205a1ff54d06353", size = 3459259, upload-time = "2026-07-24T06:21:48.881Z" }, - { url = "https://files.pythonhosted.org/packages/9f/f6/c1ad54ec35fcd5c5a5808d8b5b6319d5ae96acc63f7b5c1f7812f8cc3f71/cython-3.2.9-cp311-cp311-win_amd64.whl", hash = "sha256:efd54fe07f808e7e82f6a04f370457ca02e770c948f0e2f83345ccc7ec8bb829", size = 2789027, upload-time = "2026-07-24T06:21:50.551Z" }, - { url = "https://files.pythonhosted.org/packages/fd/37/c74d842306c8fe381c415b37460d5e3086a820fac72b8ff5cb48513ccfcd/cython-3.2.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:114b2dee0fa1daa48a59574d848da0ff1b6bdb725a755e9b92fad14962e1ff8d", size = 3009571, upload-time = "2026-07-24T06:21:52.534Z" }, - { url = "https://files.pythonhosted.org/packages/15/c0/f7c42b161edd585e3ae556fd62a2c72cd80a6ed527a9907f0c5c6fb060de/cython-3.2.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5cd9c5f138cb052130b40ad3b6976d2180c35348410995812678f4636bd8f94", size = 3183562, upload-time = "2026-07-24T06:21:54.62Z" }, - { url = "https://files.pythonhosted.org/packages/56/1b/c04520ac7f3157aa12a69b632c16261170dac9fab6c48608cc004b8f1b17/cython-3.2.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23e80bc885c599e72072e18d0746df82d394b73100c1e153cda7359e6e59fe09", size = 3354811, upload-time = "2026-07-24T06:21:56.72Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b5/4ce33235a25b19fcd51dc639f0f403b783a3b7f9b1934eade0d993fbe029/cython-3.2.9-cp312-cp312-win_amd64.whl", hash = "sha256:4b1fd5a9c03f72a18618668a8e90d569442ed742f910e3ad003dcc9348e9598b", size = 2778077, upload-time = "2026-07-24T06:21:58.7Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4a/342312c5fe021c8e0c386e1915d138e0902c48ae179b0374ab04773a8831/cython-3.2.9-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:944dc8747f640b3527649c566a5fc75ee0c15e80642ea2fdae4fe6378e1a9d4a", size = 2899729, upload-time = "2026-07-24T06:22:24.877Z" }, - { url = "https://files.pythonhosted.org/packages/f0/62/ea919ee426cb4d435ec8155e1ee6bcbb46b20d8f070527191b59769d4e7f/cython-3.2.9-cp39-abi3-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4b871ad97dd7fb1cbf56f6238c54423febd310afc1d9d9bc70c69c89b7ce57fc", size = 3226650, upload-time = "2026-07-24T06:22:26.947Z" }, - { url = "https://files.pythonhosted.org/packages/18/02/057b4f63e2ced8c3cf217c4e9fb544bfe48145f493347c7ca3f51607526c/cython-3.2.9-cp39-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9b6ebc6c74b4318eaa4e51e520dc8b95ebc7b262953c3ecb24131104681f14e", size = 2881919, upload-time = "2026-07-24T06:22:29.318Z" }, - { url = "https://files.pythonhosted.org/packages/64/e4/e158793ee3de7e4417ba17e7ff1015d6e2cf557cb485ad270b2446c9d1c7/cython-3.2.9-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:92989da161a7d18a7ad4baebc49289b2b77556d5a94916f90140ba26aecf6892", size = 3004702, upload-time = "2026-07-24T06:22:31.535Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c0/d5bbbd743ab4feddb24a7e823b34c3ec4ebab91ff503d16743a4e7ce106b/cython-3.2.9-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:e75ec625d8f8781ced690b7a2f5c2d138067711cf24bb8fb68c872c30c2fefe5", size = 2902695, upload-time = "2026-07-24T06:22:33.525Z" }, - { url = "https://files.pythonhosted.org/packages/d6/2f/80850817395985259f135baa510d9186d3a325df81cd1862060bba977029/cython-3.2.9-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:2b1756ddc3bc0cd4341a515fc420c3e25e13c249f5537159b3fb0bff8d19e55c", size = 3241554, upload-time = "2026-07-24T06:22:35.667Z" }, - { url = "https://files.pythonhosted.org/packages/4c/ce/6be776814f6cb81751f3da737ed537385738148e1ea99f89fb4637799198/cython-3.2.9-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7d41baea51ea00f9237f75af498577827493bca5e9b45bbd4e351543727e589a", size = 3124337, upload-time = "2026-07-24T06:22:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/e2/48/27c948cbbfe6050994e67a497ae530955dcda7e79084319321c49969e0fd/cython-3.2.9-cp39-abi3-win32.whl", hash = "sha256:61d4abbf84f77c8d19361d05d9f51d65d8d95e74f736eae55fa1aed8a1430469", size = 2435609, upload-time = "2026-07-24T06:22:40.005Z" }, - { url = "https://files.pythonhosted.org/packages/17/ef/cf0e1bd7542296f1752be63b027f90271448d8c8062eac66d8e44a79b883/cython-3.2.9-cp39-abi3-win_arm64.whl", hash = "sha256:57a6a78d14f7dd7d6062d9bca694e2a8c1c14113b6ceceea076abcd1161fdc5a", size = 2458025, upload-time = "2026-07-24T06:22:41.973Z" }, - { url = "https://files.pythonhosted.org/packages/00/ec/e61deec9bcfbb0e1b36f8b5ba75cb44644419b4bfd0fdd666bffd21d9579/cython-3.2.9-py3-none-any.whl", hash = "sha256:a2b0e87f6b80790c929308ca0831d686f7a180feab684fe8cd4a4380bd96aaca", size = 1259272, upload-time = "2026-07-24T06:21:18.95Z" }, -] - [[package]] name = "dash" version = "4.0.0" @@ -1736,27 +1681,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, ] -[[package]] -name = "diffusers" -version = "0.39.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "httpx" }, - { name = "huggingface-hub" }, - { name = "importlib-metadata" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pillow" }, - { name = "regex" }, - { name = "requests" }, - { name = "safetensors" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1a/81/6095237b86a3116c4789f28c4435d5296c00c0fc74ffde99008fd6b3a36c/diffusers-0.39.0.tar.gz", hash = "sha256:14bb1d98c85a0e463d734c99aaa73b480a7bc9bad22af30fbf730ef8f09c1d67", size = 4651240, upload-time = "2026-07-03T08:48:47.904Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/3f/7469c46e9d22307ea686bab687d70e6bf328722952f9d10339f5e913e608/diffusers-0.39.0-py3-none-any.whl", hash = "sha256:912aca51b5787365110806e984d5555735bf8a461073bb8459029d0bca7870ef", size = 5631176, upload-time = "2026-07-03T08:48:45.337Z" }, -] - [[package]] name = "dill" version = "0.4.1" @@ -1967,7 +1891,7 @@ base = [ { name = "transformers", extra = ["torch"] }, { name = "ultralytics" }, { name = "uvicorn" }, - { name = "yourdfpy" }, + { name = "yourdfpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] control = [ { name = "a750-control", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, @@ -1993,15 +1917,6 @@ dds = [ drone = [ { name = "pymavlink" }, ] -graspgenx = [ - { name = "graspgenx" }, - { name = "huggingface-hub" }, - { name = "matplotlib" }, - { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'x86_64' and sys_platform == 'linux') or (sys_platform != 'linux' and sys_platform != 'win32')" }, - { name = "torch", version = "2.7.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or sys_platform == 'win32'" }, - { name = "torchvision", version = "0.21.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'x86_64' and sys_platform == 'linux') or (sys_platform != 'linux' and sys_platform != 'win32')" }, - { name = "torchvision", version = "0.22.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or sys_platform == 'win32'" }, -] learning = [ { name = "h5py" }, { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -2158,7 +2073,7 @@ unitree = [ { name = "ultralytics" }, { name = "unitree-webrtc-connect" }, { name = "uvicorn" }, - { name = "yourdfpy" }, + { name = "yourdfpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] unitree-dds = [ { name = "aioquic" }, @@ -2199,12 +2114,12 @@ unitree-dds = [ { name = "unitree-sdk2py-dimos" }, { name = "unitree-webrtc-connect" }, { name = "uvicorn" }, - { name = "yourdfpy" }, + { name = "yourdfpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] visualization = [ { name = "dimos-viewer" }, { name = "rerun-sdk" }, - { name = "yourdfpy" }, + { name = "yourdfpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] web = [ { name = "aioquic" }, @@ -2459,10 +2374,8 @@ requires-dist = [ { name = "gdown", marker = "extra == 'misc'", specifier = ">=5.2.2" }, { name = "gitpython", specifier = ">=3.1.45" }, { name = "googlemaps", marker = "extra == 'misc'", specifier = ">=4.10.0" }, - { name = "graspgenx", marker = "extra == 'graspgenx'", git = "https://github.com/NVlabs/GraspGenX.git?rev=b9429097728cb1c430dd78b92edf17ba318aad03" }, { name = "gtsam-extended", marker = "(python_full_version >= '3.11' and platform_machine != 'arm64' and extra == 'mapping') or (python_full_version >= '3.11' and sys_platform != 'darwin' and extra == 'mapping') or (platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'mapping')", specifier = ">=4.3a1.post1" }, { name = "h5py", marker = "extra == 'learning'" }, - { name = "huggingface-hub", marker = "extra == 'graspgenx'", specifier = ">=0.30,<1" }, { name = "hydra-core", marker = "extra == 'perception'", specifier = ">=1.3.0" }, { name = "imagecodecs", specifier = ">=2024.6.1" }, { name = "ipykernel", marker = "extra == 'misc'" }, @@ -2480,7 +2393,6 @@ requires-dist = [ { name = "llvmlite", specifier = ">=0.42.0" }, { name = "lz4", specifier = ">=4.4.5" }, { name = "manifold3d", marker = "extra == 'apriltag'", specifier = ">=2.5.0" }, - { name = "matplotlib", marker = "extra == 'graspgenx'", specifier = ">=3.7.1" }, { name = "matplotlib", marker = "extra == 'planning'", specifier = ">=3.7.1" }, { name = "mcap", marker = "extra == 'unitree-dds'", specifier = ">=1.2.0" }, { name = "moondream", marker = "extra == 'perception'" }, @@ -2542,11 +2454,7 @@ requires-dist = [ { name = "textual-serve", specifier = ">=1.1.1,<2" }, { name = "timm", marker = "extra == 'perception'", specifier = ">=1.0.15" }, { name = "toolz", specifier = ">=1.1.0" }, - { name = "torch", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'graspgenx') or (sys_platform == 'win32' and extra == 'graspgenx')", specifier = ">=2.1,<2.7", index = "https://download.pytorch.org/whl/cu128" }, - { name = "torch", marker = "(platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'graspgenx') or (sys_platform != 'linux' and sys_platform != 'win32' and extra == 'graspgenx')", specifier = ">=2.1,<2.7" }, { name = "torchreid", marker = "extra == 'misc'", specifier = "==0.2.5" }, - { name = "torchvision", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'graspgenx') or (sys_platform == 'win32' and extra == 'graspgenx')", specifier = ">=0.16,<0.22", index = "https://download.pytorch.org/whl/cu128" }, - { name = "torchvision", marker = "(platform_machine != 'x86_64' and sys_platform == 'linux' and extra == 'graspgenx') or (sys_platform != 'linux' and sys_platform != 'win32' and extra == 'graspgenx')", specifier = ">=0.16,<0.22" }, { name = "transformers", extras = ["torch"], marker = "extra == 'perception'", specifier = ">=4.53.0,<4.54" }, { name = "trimesh", marker = "extra == 'apriltag'", specifier = ">=4.0.0" }, { name = "trimesh", marker = "extra == 'planning'" }, @@ -2565,7 +2473,7 @@ requires-dist = [ { name = "yourdfpy", marker = "(platform_machine != 'aarch64' and extra == 'visualization') or (sys_platform != 'linux' and extra == 'visualization')", specifier = ">=0.0.60" }, { name = "yourdfpy", marker = "extra == 'planning'", specifier = ">=0.0.60" }, ] -provides-extras = ["misc", "spot", "visualization", "learning", "agents", "web", "perception", "unitree", "unitree-dds", "control", "planning", "manipulation", "cpu", "cuda", "sim", "mapping", "drone", "dds", "webrtc", "base", "apriltag", "scene", "graspgenx", "all"] +provides-extras = ["misc", "spot", "visualization", "learning", "agents", "web", "perception", "unitree", "unitree-dds", "control", "planning", "manipulation", "cpu", "cuda", "sim", "mapping", "drone", "dds", "webrtc", "base", "apriltag", "scene", "all"] [package.metadata.requires-dev] autofix = [{ name = "ruff", specifier = "==0.14.3" }] @@ -2809,76 +2717,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] -[[package]] -name = "dm-control" -version = "1.0.43" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "absl-py" }, - { name = "dm-env" }, - { name = "dm-tree" }, - { name = "glfw" }, - { name = "labmaze" }, - { name = "lxml" }, - { name = "mujoco" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "protobuf" }, - { name = "pyopengl" }, - { name = "pyparsing" }, - { name = "requests" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "setuptools" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f0/eb/dcb0528623f58196522d74e7b2ec4a0cb5e890b9c5d28c7730de9e346d1a/dm_control-1.0.43.tar.gz", hash = "sha256:8f0e27246939cbafb3ca37d5a620056b77a95b5b8ba061b33a7aaca3d2185338", size = 56276100, upload-time = "2026-06-22T18:55:42.307Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/50/4d9a4ea0ceb5c9399412834877fba79acb4438bac9c520a9cab57513a93b/dm_control-1.0.43-py3-none-any.whl", hash = "sha256:4d79532c44fb7660825b1c201d0172b14f0c8585a1f39f6cbc0181ee16e5f8e2", size = 56446782, upload-time = "2026-06-22T18:55:36.927Z" }, -] - -[[package]] -name = "dm-env" -version = "1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "absl-py" }, - { name = "dm-tree" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/62/c9/93e8d6239d5806508a2ee4b370e67c6069943ca149f59f533923737a99b7/dm-env-1.6.tar.gz", hash = "sha256:a436eb1c654c39e0c986a516cee218bea7140b510fceff63f97eb4fcff3d93de", size = 20187, upload-time = "2022-12-21T00:25:29.306Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/7e/36d548040e61337bf9182637a589c44da407a47a923ee88aec7f0e89867c/dm_env-1.6-py3-none-any.whl", hash = "sha256:0eabb6759dd453b625e041032f7ae0c1e87d4eb61b6a96b9ca586483837abf29", size = 26339, upload-time = "2022-12-21T00:25:37.128Z" }, -] - -[[package]] -name = "dm-tree" -version = "0.1.10" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "absl-py" }, - { name = "attrs" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/66/a3ec619d22b6baffa5ab853e8dc6ec9d0c837127948af59bb15b988d7312/dm_tree-0.1.10.tar.gz", hash = "sha256:22f37b599e01cc3402a17f79c257a802aebd8d326de05b54657650845956208a", size = 35748, upload-time = "2026-03-31T17:35:39.03Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/76/781bc1a8ce0f4be153755e36be547d7d36964fb6d265b9b29503ed3e0a0f/dm_tree-0.1.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b8b606661abcb5336e60ce4cd6f3bec794ec794f91d8de001c1ea451dd7a7411", size = 311543, upload-time = "2026-03-31T17:35:04.766Z" }, - { url = "https://files.pythonhosted.org/packages/7c/b9/2f6278c07728c60411d363aab1b5de53b75bb3bdf27032e871c240f3b4de/dm_tree-0.1.10-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fcf11edc379723b8a17b76b6f63643e9280d55f23c37994805bf27282074192", size = 181202, upload-time = "2026-03-31T17:35:06.266Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d6/2b88e4eb5e3e5ecf9d61afe55e463ce7c9e4d1dc6630b9f7038a62e548d1/dm_tree-0.1.10-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7644b24bb5c7810b601f4b28cf6e4b516da96713ebfd9e45585240318c6b9384", size = 183874, upload-time = "2026-03-31T17:35:07.746Z" }, - { url = "https://files.pythonhosted.org/packages/49/9f/6fea8ba8cb69136af0511868bc41dfb88d0b8c3a85497dbbf16271cd84a7/dm_tree-0.1.10-cp310-cp310-win_amd64.whl", hash = "sha256:d7f42b2148a1a3758230fbf93c06b9475e5d4bd62a4d19eacafa8210b03421ac", size = 110740, upload-time = "2026-03-31T17:35:09.045Z" }, - { url = "https://files.pythonhosted.org/packages/87/dc/b01d0f70cde99b306731216a98287ba5926a50f27222f2ada0b99ad0911f/dm_tree-0.1.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8218af7b99701bb8b03001c82961dc2cf81d7a734958206d2ea1ede8fbbe2b5f", size = 314603, upload-time = "2026-03-31T17:35:10.052Z" }, - { url = "https://files.pythonhosted.org/packages/40/72/3bafa58492862360113c1cccb26747c7863d417271e1572bacb3c281162f/dm_tree-0.1.10-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cacef6180fcfef30bab2cac5164e753e2f7a2e60e5da0feb81f2d318416f8d98", size = 182657, upload-time = "2026-03-31T17:35:11.462Z" }, - { url = "https://files.pythonhosted.org/packages/78/10/587a2cdc05995069aa63b659d884eb3e58a3c86a5b4a00acdb7a316bddf3/dm_tree-0.1.10-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0e8907bb6809dc195be3af077e382126eaebe06c00f835d09ae26e36d2165ff", size = 185008, upload-time = "2026-03-31T17:35:12.838Z" }, - { url = "https://files.pythonhosted.org/packages/60/0e/08d938d84cbf791dde009b3d3a6637f27a0004235e700641a0ac038daac5/dm_tree-0.1.10-cp311-cp311-win_amd64.whl", hash = "sha256:a1c82dd4726a16ac6b6f7a77a5fb097ee396fd349ae301407eb5736f15b8fa16", size = 111472, upload-time = "2026-03-31T17:35:14.035Z" }, - { url = "https://files.pythonhosted.org/packages/34/a1/17e0d68eec978c483db4712b14d083ee01484381b29ea85edb2b20210bd0/dm_tree-0.1.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94af18e4fd22ce69eccae89eeed8ed498b6b4cc4957f4ed10b4160e59f620e1d", size = 315976, upload-time = "2026-03-31T17:35:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6f/ed603715fbc29c887a8985252e2cfe0d449497aea96bac51010159771617/dm_tree-0.1.10-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b442a0c1e9d0960e0314a2e4af81fd328a87921b6d6db6dc41bfa420536884d6", size = 184053, upload-time = "2026-03-31T17:35:16.512Z" }, - { url = "https://files.pythonhosted.org/packages/83/eb/1d55c679cee9a54e552480d308535753c72e2250cf720d7aa777bff2a4fe/dm_tree-0.1.10-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:012c2b376e88d3685c73a4b5c23be41fe933e14e380dcd90172971690b0e02d2", size = 186506, upload-time = "2026-03-31T17:35:17.593Z" }, - { url = "https://files.pythonhosted.org/packages/89/2d/adef6924f8dc7f1665eea4ce066387820c14a629d0e1005568892d56ea6a/dm_tree-0.1.10-cp312-cp312-win_amd64.whl", hash = "sha256:da8d5b8995bea1b6bb93f457e0dad5d16e6e2344a6488ced55320e7f3fd50f56", size = 112708, upload-time = "2026-03-31T17:35:18.699Z" }, -] - [[package]] name = "dnspython" version = "2.8.0" @@ -3033,6 +2871,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/09/f8d8f8f31e4483c10a906437b4ce31bdf3d6d417b73fe33f1a8b59e34228/einops-0.8.2-py3-none-any.whl", hash = "sha256:54058201ac7087911181bfec4af6091bb59380360f069276601256a76af08193", size = 65638, upload-time = "2026-01-26T04:13:18.546Z" }, ] +[[package]] +name = "embreex" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/89/692237d6a60f58f87f7bffdfe8c30008efd8811411e692f96d2cb1df0818/embreex-4.4.0-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:b685ed766ef86dbd302ae9e92ab20156007c31cbd2ea5465542b50eea99c6da5", size = 5098460, upload-time = "2026-04-22T19:46:30.276Z" }, + { url = "https://files.pythonhosted.org/packages/14/fe/cedef264696768248f8139c569e10796ffb76627383adb5a60e1eaf28a20/embreex-4.4.0-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:daf8b1848798523d7d71cc22f2610401ab02ec93ea063f9cbb90dcb9abda2ccf", size = 13215487, upload-time = "2026-04-22T19:46:32.769Z" }, + { url = "https://files.pythonhosted.org/packages/11/f5/460b7f79689ac5e6ceb3ec2a1194176a0a66d6c4e010dae68ba899a1c927/embreex-4.4.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f548cbebc550624ef08530ed9dcd147eeddcd181fd8f32cf3378800b39b21034", size = 14438524, upload-time = "2026-04-22T19:46:35.533Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c2/aef3606d7ca2b4d2d18e57c8f65762b94d253e678a05c946649bb1913f5e/embreex-4.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:58921ef7ad488dbd514b3053e7c4a9fdcd2f7008426b3185b9f1bd394c608edd", size = 13119003, upload-time = "2026-04-22T19:46:38.442Z" }, + { url = "https://files.pythonhosted.org/packages/d8/e1/84b02da29deac092349b12fae21a3cbf4b64104ef78e0d0c1bca5d268112/embreex-4.4.0-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b2e69b777c0b7878e13ad8c31f4fecaddb5cd8633a5814c1ac11da2efe1065dc", size = 5098210, upload-time = "2026-04-22T19:46:41.149Z" }, + { url = "https://files.pythonhosted.org/packages/4c/0c/1bcdcb8cb09713d40c3cc6d303a4e456113df859dacb28a1b7af8a19a718/embreex-4.4.0-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:96a187753f578cb685051f8fd679dc7986f887fcb922bb81a9924f5b89e941d8", size = 13214875, upload-time = "2026-04-22T19:46:43.439Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/bed6f27a57b89ad028c8ec5adf6f1877a1ef92d983d703abb7a70717f0d9/embreex-4.4.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8415d14c8956afec7eb05749f1e0bf396bb51efd566054ca169e10e4089e7bb7", size = 14474358, upload-time = "2026-04-22T19:46:46.056Z" }, + { url = "https://files.pythonhosted.org/packages/21/f4/c3515fde7bacab245673988398ef40928ce0b9fb54e2b51e90a4a4535479/embreex-4.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:bfa54fb71cbb3a41c2366cabd09bb2dbbc8700843872f2c8655a3215a73459a7", size = 13119132, upload-time = "2026-04-22T19:46:48.753Z" }, + { url = "https://files.pythonhosted.org/packages/f6/04/b3413ba4f1c17f2374cc39b5b86404221aedc632c8b6cdb484697eeffcd8/embreex-4.4.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:3bb261a25c21d50bc7f046401e7256e59e43a500b229fb2a00b6393c61e5293d", size = 5097518, upload-time = "2026-04-22T19:46:51.379Z" }, + { url = "https://files.pythonhosted.org/packages/6d/78/8cc0960cc0f2d60d581869a66c8013e1bf1c73bf5bf9609bd8aa79e0f721/embreex-4.4.0-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:8e93bce7cf905365117dea2d0726d19262c88a010044d00631db6bb7dc145612", size = 13214768, upload-time = "2026-04-22T19:46:54.088Z" }, + { url = "https://files.pythonhosted.org/packages/79/b0/05a5b4d49749602b12e13d1871f8e6d1fe6db806eda75f6f57bb4f1acf6f/embreex-4.4.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cb0872f5bc231f465b840b122847acbaf468ac48f49bfaff127c5347ec0db94f", size = 14529899, upload-time = "2026-04-22T19:46:56.824Z" }, + { url = "https://files.pythonhosted.org/packages/b5/96/625e035f3433071c91de07e66265a261be7bb708367f785000f93d7a992a/embreex-4.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:6c092ee1adf5c48b7430c7ae3902943863745e54b4aef4327ecb3473e0a299d7", size = 13119305, upload-time = "2026-04-22T19:46:59.329Z" }, +] + [[package]] name = "etils" version = "1.13.0" @@ -3322,7 +3183,6 @@ resolution-markers = [ dependencies = [ { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "msgpack", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "optax", marker = "python_full_version < '3.11'" }, { name = "orbax-checkpoint", marker = "python_full_version < '3.11'" }, { name = "pyyaml", marker = "python_full_version < '3.11'" }, @@ -3421,20 +3281,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c4/73/3a3e6cb864ddf98800a9236ad497d32e5b50eb1682ac659f7d669d92faec/foxglove_websocket-0.1.4-py3-none-any.whl", hash = "sha256:772e24e2c98bdfc704df53f7177c8ff5bab0abc4dac59a91463aca16debdd83a", size = 14392, upload-time = "2025-07-14T20:26:26.899Z" }, ] -[[package]] -name = "freetype-py" -version = "2.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/9c/61ba17f846b922c2d6d101cc886b0e8fb597c109cedfcb39b8c5d2304b54/freetype-py-2.5.1.zip", hash = "sha256:cfe2686a174d0dd3d71a9d8ee9bf6a2c23f5872385cf8ce9f24af83d076e2fbd", size = 851738, upload-time = "2024-08-29T18:32:26.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/a8/258dd138ebe60c79cd8cfaa6d021599208a33f0175a5e29b01f60c9ab2c7/freetype_py-2.5.1-py3-none-macosx_10_9_universal2.whl", hash = "sha256:d01ded2557694f06aa0413f3400c0c0b2b5ebcaabeef7aaf3d756be44f51e90b", size = 1747885, upload-time = "2024-08-29T18:32:17.604Z" }, - { url = "https://files.pythonhosted.org/packages/a2/93/280ad06dc944e40789b0a641492321a2792db82edda485369cbc59d14366/freetype_py-2.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d2f6b3d68496797da23204b3b9c4e77e67559c80390fc0dc8b3f454ae1cd819", size = 1051055, upload-time = "2024-08-29T18:32:19.153Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/853cad240ec63e21a37a512ee19c896b655ce1772d803a3dd80fccfe63fe/freetype_py-2.5.1-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:289b443547e03a4f85302e3ac91376838e0d11636050166662a4f75e3087ed0b", size = 1043856, upload-time = "2024-08-29T18:32:20.565Z" }, - { url = "https://files.pythonhosted.org/packages/93/6f/fcc1789e42b8c6617c3112196d68e87bfe7d957d80812d3c24d639782dcb/freetype_py-2.5.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:cd3bfdbb7e1a84818cfbc8025fca3096f4f2afcd5d4641184bf0a3a2e6f97bbf", size = 1108180, upload-time = "2024-08-29T18:32:21.871Z" }, - { url = "https://files.pythonhosted.org/packages/2a/1b/161d3a6244b8a820aef188e4397a750d4a8196316809576d015f26594296/freetype_py-2.5.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:3c1aefc4f0d5b7425f014daccc5fdc7c6f914fb7d6a695cc684f1c09cd8c1660", size = 1106792, upload-time = "2024-08-29T18:32:23.134Z" }, - { url = "https://files.pythonhosted.org/packages/93/6e/bd7fbfacca077bc6f34f1a1109800a2c41ab50f4704d3a0507ba41009915/freetype_py-2.5.1-py3-none-win_amd64.whl", hash = "sha256:0b7f8e0342779f65ca13ef8bc103938366fecade23e6bb37cb671c2b8ad7f124", size = 814608, upload-time = "2024-08-29T18:32:24.648Z" }, -] - [[package]] name = "frozenlist" version = "1.8.0" @@ -3652,50 +3498,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/fe/26/bca4d737a9acea25e94c19940a780bbf0be64a691f7caf3a68467d3a5838/googlemaps-4.10.0.tar.gz", hash = "sha256:3055fcbb1aa262a9159b589b5e6af762b10e80634ae11c59495bd44867e47d88", size = 33056, upload-time = "2023-01-26T16:45:02.501Z" } -[[package]] -name = "graspgenx" -version = "1.0.0" -source = { git = "https://github.com/NVlabs/GraspGenX.git?rev=b9429097728cb1c430dd78b92edf17ba318aad03#b9429097728cb1c430dd78b92edf17ba318aad03" } -dependencies = [ - { name = "addict" }, - { name = "diffusers" }, - { name = "h5py" }, - { name = "huggingface-hub" }, - { name = "hydra-core" }, - { name = "imageio" }, - { name = "matplotlib" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pyopengl" }, - { name = "pyrender" }, - { name = "pytest" }, - { name = "pyyaml" }, - { name = "scene-synthesizer", extra = ["recommend"] }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "setuptools" }, - { name = "sharedarray" }, - { name = "tensorboard" }, - { name = "tensorboardx" }, - { name = "tensordict" }, - { name = "timm" }, - { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'x86_64' and sys_platform == 'linux') or (sys_platform != 'linux' and sys_platform != 'win32')" }, - { name = "torch", version = "2.7.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or sys_platform == 'win32'" }, - { name = "torch-geometric" }, - { name = "torchvision", version = "0.21.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'x86_64' and sys_platform == 'linux') or (sys_platform != 'linux' and sys_platform != 'win32')" }, - { name = "torchvision", version = "0.22.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or sys_platform == 'win32'" }, - { name = "tqdm" }, - { name = "transformers" }, - { name = "trimesh" }, - { name = "urdfpy" }, - { name = "viser" }, - { name = "webdataset" }, - { name = "yapf" }, - { name = "yourdfpy" }, -] - [[package]] name = "greenlet" version = "3.5.3" @@ -4774,35 +4576,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/46/2c/5c160dbdef7123f8cc97fd8ece7e0198627a426a2a49614845e9086feb8d/kubernetes-36.0.2-py2.py3-none-any.whl", hash = "sha256:faf9b5241b58de0c4a5069f2a0ffc8ac06fece7215156cd3d3ba081a78a858b6", size = 4617568, upload-time = "2026-06-01T18:20:28.737Z" }, ] -[[package]] -name = "labmaze" -version = "1.0.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "absl-py" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/0a/139c4ae896b9413bd4ca69c62b08ee98dcfc78a9cbfdb7cadd0dce2ad31d/labmaze-1.0.6.tar.gz", hash = "sha256:2e8de7094042a77d6972f1965cf5c9e8f971f1b34d225752f343190a825ebe73", size = 4670455, upload-time = "2022-12-05T18:42:43.566Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/0c/6a3941f48644c0b9305c7a22bd51974be1fed8e9233b16c893d728805143/labmaze-1.0.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b2ddef976dfd8d992b19cfa6c633f2eba7576d759c2082da534e3f727479a84a", size = 4815423, upload-time = "2022-12-05T18:41:47.351Z" }, - { url = "https://files.pythonhosted.org/packages/d0/fe/b038c6a15732eb064767dc92ca39a38b2f5df183576384f0cfb6a4840f69/labmaze-1.0.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:157efaa93228c8ccce5cae337902dd652093e0fba9d3a0f6506e4bee272bb66f", size = 4806825, upload-time = "2022-12-05T18:41:49.922Z" }, - { url = "https://files.pythonhosted.org/packages/59/ec/2762281d4f26845b20bb7529742a6914fcb07c8e7c522175b879df0127cf/labmaze-1.0.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3ce98b9541c5fe6a306e411e7d018121dd646f2c9978d763fad86f9f30c5f57", size = 4871532, upload-time = "2022-12-05T18:41:52.784Z" }, - { url = "https://files.pythonhosted.org/packages/4d/93/abac7877e1d7de984a2f0f5be561ff0dc795ae7e22595cf2f7c7032cd27e/labmaze-1.0.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e6433bd49bc541791de8191040526fddfebb77151620eb04203453f43ee486a", size = 4875892, upload-time = "2022-12-05T18:41:55.603Z" }, - { url = "https://files.pythonhosted.org/packages/c4/10/5262db11b3c1db8e4fbc3feed9baed4f95db6047b8d9dcaf4f9fb8da9ba3/labmaze-1.0.6-cp310-cp310-win_amd64.whl", hash = "sha256:6a507fc35961f1b1479708e2716f65e0d0611cefb55f31a77be29ce2339b6fef", size = 4812953, upload-time = "2022-12-05T18:41:58.098Z" }, - { url = "https://files.pythonhosted.org/packages/6d/3c/cdc95db2aa8cd80c193b7b30b9a9be071897c4f0b558d5fc007b1adf74c3/labmaze-1.0.6-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a0c2cb9dec971814ea9c5d7150af15fa3964482131fa969e0afb94bd224348af", size = 4815406, upload-time = "2022-12-05T18:42:00.412Z" }, - { url = "https://files.pythonhosted.org/packages/75/46/eb96e23ccddd40f403cea3f9f5d15eae7759317a1762b761692541edd6d9/labmaze-1.0.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2c6ba9538d819543f4be448d36b4926a3881e53646a2b331ebb5a1f353047d05", size = 4806777, upload-time = "2022-12-05T18:42:02.345Z" }, - { url = "https://files.pythonhosted.org/packages/0d/7e/787e0d3c17e29a46484158460e21fcf5cd7a076c81b2ec31807f2753ea43/labmaze-1.0.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70635d1cdb0147a02efb6b3f607a52cdc51723bc3dcc42717a0d4ef55fa0a987", size = 4871563, upload-time = "2022-12-05T18:42:04.538Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ce/be3952d7036b009f6dd004b6f5dfe97bbff79572ef0cf56a734aaead030f/labmaze-1.0.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff472793238bd9b6dabea8094594d6074ad3c111455de3afcae72f6c40c6817e", size = 4875913, upload-time = "2022-12-05T18:42:06.969Z" }, - { url = "https://files.pythonhosted.org/packages/50/a5/8c9f9be038401a31f9f87bd44f28c8edff63c0c3f1168ca882e351215761/labmaze-1.0.6-cp311-cp311-win_amd64.whl", hash = "sha256:2317e65e12fa3d1abecda7e0488dab15456cee8a2e717a586bfc8f02a91579e7", size = 4813089, upload-time = "2022-12-05T18:42:09.481Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/670a6e6beeeb166aa911fe861c1a16f62a9f3cfc7b54ea4b114cc23d0380/labmaze-1.0.6-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e36b6fadcd78f22057b597c1c77823e806a0987b3bdfbf850e14b6b5b502075e", size = 4814941, upload-time = "2023-10-04T16:54:25.613Z" }, - { url = "https://files.pythonhosted.org/packages/e5/3a/47a3f83736e0b70f78b22d53e0a3230160a61e8ba6267003f25d2b24b832/labmaze-1.0.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d1a4f8de29c2c3d7f14163759b69cd3f237093b85334c983619c1db5403a223b", size = 4807545, upload-time = "2023-10-04T16:56:00.113Z" }, - { url = "https://files.pythonhosted.org/packages/ad/95/2ca4dd1efff4456f44baf4c4a980cfea6f6fb8729912a760ec9bf912876b/labmaze-1.0.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a394f8bb857fcaa2884b809d63e750841c2662a106cfe8c045f2112d201ac7d5", size = 4873133, upload-time = "2023-10-04T17:32:24.246Z" }, - { url = "https://files.pythonhosted.org/packages/f9/9c/1c928d0f5a20e4b9544d564e43ecda785f09a29ecbaa37f4e70989d0d4bd/labmaze-1.0.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d17abb69d4dfc56183afb5c317e8b2eaca0587abb3aabd2326efd3143c81f4e", size = 4875122, upload-time = "2023-10-04T17:08:11.069Z" }, - { url = "https://files.pythonhosted.org/packages/5b/0f/13f0d54305e66c14c90512f3682f713273ec9aa94d107be7947157b37a74/labmaze-1.0.6-cp312-cp312-win_amd64.whl", hash = "sha256:5af997598cc46b1929d1c5a1febc32fd56c75874fe481a2a5982c65cee8450c9", size = 4811813, upload-time = "2023-10-04T17:20:30.837Z" }, -] - [[package]] name = "langchain" version = "1.2.3" @@ -5312,6 +5085,44 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/26/91/c4ef7c6d28f9fa71cbf0ad5fcafa6d706744065df7aa6b17256f009fb6cc/manifold3d-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:08050488f36e59b39aef320d88dc32d63cdec016824dd3835ef835a2b74580ed", size = 1038017, upload-time = "2026-06-04T14:15:28.875Z" }, ] +[[package]] +name = "mapbox-earcut" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/7b/bbf6b00488662be5d2eb7a188222c264b6f713bac10dc4a77bf37a4cb4b6/mapbox_earcut-2.0.0.tar.gz", hash = "sha256:81eab6b86cf99551deb698b98e3f7502c57900e5c479df15e1bdaf1a57f0f9d6", size = 39934, upload-time = "2025-11-16T18:41:27.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/d3/5222339a8fad091bf64f2e3041e48606d69d69f0609a7632ca17a8a05d5a/mapbox_earcut-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:c9a1dab7529f8e54bdb377f908e56f1e2b9a7e27ed168c64d3c7c38ed04ac201", size = 55920, upload-time = "2025-11-16T18:40:09.254Z" }, + { url = "https://files.pythonhosted.org/packages/19/e4/88d06e83ab75db2f4ae140a1e03ad8f84b02ac8af585dd61108aba73b8ed/mapbox_earcut-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e5953098ea198253c8a40e2f282ca5b04d50ec2b9661e20c4cd2b2be39f0bb0", size = 52557, upload-time = "2025-11-16T18:40:10.536Z" }, + { url = "https://files.pythonhosted.org/packages/22/88/abefd244ea049e42334c5f7a9e3b58f4ec3c84d063119ba3c8d27ff31932/mapbox_earcut-2.0.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:efe5fd5de409e3b6d13907e73f295c8f1d63bdb6b8ca155dde4c93865796eafe", size = 56950, upload-time = "2025-11-16T18:40:11.905Z" }, + { url = "https://files.pythonhosted.org/packages/3c/e2/11122fddd086b930502eb4a954735da0f75e9d658fdab2d9e5914b9ebd2a/mapbox_earcut-2.0.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd04da6edbca1dd68ddbfac2398a95c763f35d7317fed227fde5b3aff1253b18", size = 59618, upload-time = "2025-11-16T18:40:13.017Z" }, + { url = "https://files.pythonhosted.org/packages/e8/fd/e62195729daa3111fe95404a99c7a6b3aa174800373d10111b7e7278a789/mapbox_earcut-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bdb8881e857d6d9277df696e9cfb8749c00d6162021d9359cba9da58dfdd4f5", size = 153021, upload-time = "2025-11-16T18:40:14.294Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6a/d39ebaaa9010ea6c9f4d468f8812b1a1b31a40fba4f02ff29bc1bf321c30/mapbox_earcut-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6e2d1bf5af90d5857955775b4d8ea15b02e172f2a8f194bba50ff95f8ff3e80e", size = 157736, upload-time = "2025-11-16T18:40:16.344Z" }, + { url = "https://files.pythonhosted.org/packages/20/00/6a59cdb8d8c1bf7e3cc92f0404f68fdb1a3cb0bbb0837af0dbb93d6290a6/mapbox_earcut-2.0.0-cp310-cp310-win32.whl", hash = "sha256:5b0aa63dd890d712343095b05eb7b60e071912ad3ced1fc4187d6a6a739677bc", size = 51564, upload-time = "2025-11-16T18:40:17.852Z" }, + { url = "https://files.pythonhosted.org/packages/bc/7b/af69669c959d8f7fd1bd49c15deace2360bf6a79dad7bf9f7a7f1c137da6/mapbox_earcut-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b1355f13af89ea815b32f59a5455db295c965d51ab501bde0459cddc010a7149", size = 56793, upload-time = "2025-11-16T18:40:18.953Z" }, + { url = "https://files.pythonhosted.org/packages/07/9f/fbd15d9e348e75e986d6912c4eab99888106b7e5fb0a01e765422f7cd464/mapbox_earcut-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:9b5040e79e3783295e99c90277f31c1cbaddd3335297275331995ba5680e3649", size = 55773, upload-time = "2025-11-16T18:40:20.045Z" }, + { url = "https://files.pythonhosted.org/packages/72/40/be761298704fbbaa81c5618bb306f1510fb068e482f6a1c8b3b6c1b31479/mapbox_earcut-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1cf43baafec3ef1e967319d9b5da96bc6ddf3dbb204b6f3535275eda4b519a72", size = 52444, upload-time = "2025-11-16T18:40:21.501Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0b/0c0c08db9663238ffb82c48259582dc0047a3255d98c0ac83c48026b7544/mapbox_earcut-2.0.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a283531847f603dd9d69afb75b21bd009d385ca9485fcd3e5a7fa5db1ccd913", size = 56803, upload-time = "2025-11-16T18:40:22.891Z" }, + { url = "https://files.pythonhosted.org/packages/f0/4a/86796859383d7d11fa5d4bcf1983f94c6cbb9eeb60fb3bab527fec4b32fa/mapbox_earcut-2.0.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ab697676f4cec4572d4e941b7a3429a6687bf2ac6e8db3f3781024e3239ae3a0", size = 59403, upload-time = "2025-11-16T18:40:24.021Z" }, + { url = "https://files.pythonhosted.org/packages/6c/db/adaf981ab3bcfcf993ef317636b1f27210d6834bb1e8d63db6ad7c08214a/mapbox_earcut-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1bdac76e048f4299accf4eaf797079ddfc330442e7231c15535ed198100d6c5", size = 152876, upload-time = "2025-11-16T18:40:25.588Z" }, + { url = "https://files.pythonhosted.org/packages/d2/83/86417974039e7554c9e1e55c852a7e9c2a1390d64675eb85d70e5fa7eb37/mapbox_earcut-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a6945b23f859bef11ce3194303d17bd371c86b637e7029f81b1feaff3db3758", size = 157548, upload-time = "2025-11-16T18:40:27.202Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4c/c82a292bb21e5c651d81334123db2d654c5c9d19b2197080d3429dc1e49a/mapbox_earcut-2.0.0-cp311-cp311-win32.whl", hash = "sha256:8e119524c29406afb5eaa15e933f297d35679293a3ca62ced22f97a14c484cb5", size = 51424, upload-time = "2025-11-16T18:40:28.415Z" }, + { url = "https://files.pythonhosted.org/packages/30/57/6c39d7db81f72a3e4814ef152c8fb8dfe275dc4b03c9bfa073d251e3755f/mapbox_earcut-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:378bbbb3304e446023752db8f44ecd6e7ef965bcbda36541d2ae64442ba94254", size = 56662, upload-time = "2025-11-16T18:40:29.863Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d6/a1ef6e196b3d6968bf6546d4f7e54c559f9cff8991fdb880df0ba1618f52/mapbox_earcut-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:6d249a431abd6bbff36f1fd0493247a86de962244cc4081b4d5050b02ed48fb1", size = 50505, upload-time = "2025-11-16T18:40:30.992Z" }, + { url = "https://files.pythonhosted.org/packages/8d/93/846804029d955c3c841d8efff77c2b0e8d9aab057d3a077dc8e3f88b5ea4/mapbox_earcut-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db55ce18e698bc9d90914ee7d4f8c3e4d23827456ece7c5d7a1ec91e90c7122b", size = 55623, upload-time = "2025-11-16T18:40:32.113Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f6/cc9ece104bc3876b350dba6fef7f34fb7b20ecc028d2cdbdbecb436b1ed1/mapbox_earcut-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01dd6099d16123baf582a11b2bd1d59ce848498cf0cdca3812fd1f8b20ff33b7", size = 52028, upload-time = "2025-11-16T18:40:33.516Z" }, + { url = "https://files.pythonhosted.org/packages/88/6e/230da4aabcc56c99e9bddb4c43ce7d4ba3609c0caf2d316fb26535d7c60c/mapbox_earcut-2.0.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d5a098aae26a52282bc981a38e7bf6b889d2ea7442f2cd1903d2ba842f4ff07", size = 56351, upload-time = "2025-11-16T18:40:35.217Z" }, + { url = "https://files.pythonhosted.org/packages/1a/f7/5cdd3752526e91d91336c7263af7767b291d21e63c89d7190a60051f0f87/mapbox_earcut-2.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de35f241d0b9110ad9260f295acedd9d7cc0d7acfe30d36b1b3ee8419c2caba1", size = 59209, upload-time = "2025-11-16T18:40:36.634Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a2/b7781416cb93b37b95d0444e03f87184de8815e57ff202ce4105fa921325/mapbox_earcut-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cb63ab85e2e430c350f93e75c13f8b91cb8c8a045f3cd714c390b69a720368a", size = 152316, upload-time = "2025-11-16T18:40:38.147Z" }, + { url = "https://files.pythonhosted.org/packages/c1/74/396338e3d345e4e36fb23a0380921098b6a95ce7fb19c4777f4185a5974e/mapbox_earcut-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fb3c9f069fc3795306db87f8139f70c4f047532f897a3de05f54dc1faebc97f6", size = 157268, upload-time = "2025-11-16T18:40:39.753Z" }, + { url = "https://files.pythonhosted.org/packages/56/2c/66fd137ea86c508f6cd7247f7f6e2d1dabffc9f0e9ccf14c71406b197af1/mapbox_earcut-2.0.0-cp312-cp312-win32.whl", hash = "sha256:eb290e6676217707ed238dd55e07b0a8ca3ab928f6a27c4afefb2ff3af08d7cb", size = 51226, upload-time = "2025-11-16T18:40:41.018Z" }, + { url = "https://files.pythonhosted.org/packages/b8/84/7b78e37b0c2109243c0dad7d9ba9774b02fcee228bf61cf727a5aa1702e2/mapbox_earcut-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:5ef5b3319a43375272ad2cad9333ed16e569b5102e32a4241451358897e6f6ee", size = 56417, upload-time = "2025-11-16T18:40:42.173Z" }, + { url = "https://files.pythonhosted.org/packages/75/7f/cd7195aa27c1c8f2b9d38025a5a8663f32cd01c07b648a54b1308ab26c15/mapbox_earcut-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4a3706feb5cc8c782d8f68bb0110c8d551304043f680a87a54b0651a2c208c3", size = 50111, upload-time = "2025-11-16T18:40:43.334Z" }, +] + [[package]] name = "markdown" version = "3.10.2" @@ -8135,15 +7946,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/90/7d766d54bb95939725e9a9361f9c06b0cfbe3fe100aa35400f0a461a278a/pygame-2.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:3a9e7396be0d9633831c3f8d5d82dd63ba373ad65599628294b7a4f8a5a01a65", size = 10624591, upload-time = "2024-09-29T11:52:54.489Z" }, ] -[[package]] -name = "pyglet" -version = "2.1.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/ee/5caebffaf0345e5ccf342c30605898bd07654cee626154f7af2e465cc81c/pyglet-2.1.15.tar.gz", hash = "sha256:0ef34fe730808a97e48c24dd6ed4ff614024b980955ed8dbc2dd01ededaa08cb", size = 6597005, upload-time = "2026-06-28T11:39:09.98Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/a8/9a203001fa496934708baf6cadc54b62e453f1256d5d88589c71019dd751/pyglet-2.1.15-py3-none-any.whl", hash = "sha256:8709131baf5e96c496d38197ff9f9207ad06f273c9694570008b145c58bf51ae", size = 1036800, upload-time = "2026-06-28T11:39:04.717Z" }, -] - [[package]] name = "pygments" version = "2.19.2" @@ -8424,30 +8226,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/5e/35c856e186b74678c24927847ad9895a51f1bc02a0c6126477a6c6040064/pyreadline3-3.5.6-py3-none-any.whl", hash = "sha256:8449b734232e42a5dcd74048e39b60db2839a4c38cf3ae2bf7707d58b5389c0d", size = 85243, upload-time = "2026-05-14T17:55:03.262Z" }, ] -[[package]] -name = "pyrender" -version = "0.1.45" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "freetype-py" }, - { name = "imageio" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pillow" }, - { name = "pyglet" }, - { name = "pyopengl" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "six" }, - { name = "trimesh" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4d/5a/2a3e5bfd83071a81e02291288391e0fa2c85d1c6765357f4de2dbc27bca6/pyrender-0.1.45.tar.gz", hash = "sha256:284b2432bf6832f05c5216c4b979ceb514ea78163bf53b8ce2bdf0069cb3b92e", size = 1202386, upload-time = "2021-02-18T18:56:28.82Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/88/174c28b9d3d03cf6d8edb6f637458f30f1cf1a2bd7a617cbd9dadb1740f6/pyrender-0.1.45-py3-none-any.whl", hash = "sha256:5cf751d1f21fba4640e830cef3a0b5a95ed0f05677bf92c6b8330056b4023aeb", size = 1214061, upload-time = "2021-02-18T18:56:27.275Z" }, -] - [[package]] name = "pysocks" version = "1.7.1" @@ -8624,33 +8402,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl", hash = "sha256:f32ad10589859c11053ad7d9bb3c9695cdf862113bfb0d20bc4d890198287399", size = 59847, upload-time = "2026-02-06T23:38:04.861Z" }, ] -[[package]] -name = "python-fcl" -version = "0.7.0.11" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cython" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/37/84/0416ef5aefa28b3bd289e492c0f03019da733605f31d936466ff1ef4a373/python_fcl-0.7.0.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e821a003f47a43e4282996f16361c42f6d73d7663f99b80228909556048a4647", size = 2002571, upload-time = "2026-04-08T05:10:06.615Z" }, - { url = "https://files.pythonhosted.org/packages/95/50/90cf468312733685e1e4e4c32b1cd9fd2fd4dd039260826655b4f8a463e2/python_fcl-0.7.0.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fe1a0f453cbcb074896fe926101417b29a1e60e19844c0f2987ffe1c2315c99", size = 1567709, upload-time = "2026-04-08T05:10:08.246Z" }, - { url = "https://files.pythonhosted.org/packages/60/a2/62a6c10cc3af052a64e5c4631fec9fc3b01f9f06c59d5ee3715d44ba9c2d/python_fcl-0.7.0.11-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f17a99f5686952452e1ac56729a33d8a2659b16b29eaae2a70ebc6f2b74a6842", size = 4465498, upload-time = "2026-04-08T05:10:09.979Z" }, - { url = "https://files.pythonhosted.org/packages/bf/a4/d9cf98b28370dd98ac36468e20a516987d34267d4127b286665147bf44c9/python_fcl-0.7.0.11-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f12e028d8de44f9aa6a3b34d44ca6757c9ef9f252aa477161559fe2a91d5a47c", size = 4605211, upload-time = "2026-04-08T05:10:11.942Z" }, - { url = "https://files.pythonhosted.org/packages/57/57/1f2835b965b1adb676bc64556dd7c6ec36b91d3b399e9ce523235f8d33b5/python_fcl-0.7.0.11-cp310-cp310-win_amd64.whl", hash = "sha256:5afac7af59480ba2ca748516e877a4f344e7d80bfa7304d9e99eed0d10ef2b7d", size = 1097582, upload-time = "2026-04-08T05:10:13.48Z" }, - { url = "https://files.pythonhosted.org/packages/99/fc/45e2986cfb39a000b153cdff5e9cb404e45a20949986610013b65ea1917f/python_fcl-0.7.0.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1bd8aef44b2d1ba8a6beefcb41b9d6a797f72f8514acd0a9819fa2ed1b1ee277", size = 2002464, upload-time = "2026-04-08T05:10:14.967Z" }, - { url = "https://files.pythonhosted.org/packages/de/cf/612186d220d71c01cf07b17ecdd068d9ea0d4808f4cad994ef321fcaa4e7/python_fcl-0.7.0.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce515e599fe51e05e13df963339e75874529cb75c971b73319408acd5b2f7e5e", size = 1567431, upload-time = "2026-04-08T05:10:16.316Z" }, - { url = "https://files.pythonhosted.org/packages/72/ab/8be64abc477a3dd1da9341d610c391dd5266c9fce61871ac3901494cf059/python_fcl-0.7.0.11-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b1c7ec8b1c7a1e983a18bfb6d3a1c812c6de94f7b742f9c70b94f79397f57c14", size = 4527670, upload-time = "2026-04-08T05:10:17.697Z" }, - { url = "https://files.pythonhosted.org/packages/99/3a/38ece5c5e171e82601b98a2a728592fd906e007068070395d734d3d45744/python_fcl-0.7.0.11-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:08196821a2d903d389eb69ced3de4cc0352ed926074909bcff17d20256752d38", size = 4666250, upload-time = "2026-04-08T05:10:19.22Z" }, - { url = "https://files.pythonhosted.org/packages/d8/52/8a639d5b8650f0e88fdd296cd7e49064ea18800c3f6e99f4b02ed81591d2/python_fcl-0.7.0.11-cp311-cp311-win_amd64.whl", hash = "sha256:131f6b0621fe0a57735ec5318f7e4aa705299c568aed48ff3bb1030ffd824cd3", size = 1097590, upload-time = "2026-04-08T05:10:21.248Z" }, - { url = "https://files.pythonhosted.org/packages/1a/f1/320e1041a27460d390fe2a2cf1ce74f7fa381f1af36a6fc66aa6efe16c17/python_fcl-0.7.0.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1e39419c089477a5be17606b9ffc90a390c823f2b81cf66fb6288cdbcce79c85", size = 2000323, upload-time = "2026-04-08T05:10:23.034Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b9/b3da9b16d6213db4d932dddc82282cb539d0d85a41581f548efec7f7ae37/python_fcl-0.7.0.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47238454103b8e5e66ca285aae84aa9c2386ae4fe3f2d392f1cbabe24bd69b1a", size = 1568204, upload-time = "2026-04-08T05:10:24.924Z" }, - { url = "https://files.pythonhosted.org/packages/17/77/704a3bb182b59dbe26b9836c00ce2e53a3877a21a6e7dfbfc404ab1fefe1/python_fcl-0.7.0.11-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a69a007099d610d31290d7638750545c18c54b94c0cc1f0f420fc27740761f69", size = 4494148, upload-time = "2026-04-08T05:10:26.31Z" }, - { url = "https://files.pythonhosted.org/packages/88/5d/f13c1c8eed4ce3b9e1f59c6dd31850540e575390cd85917a74afc027a3c0/python_fcl-0.7.0.11-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ac7b02f47d7fb881e5786be8328da3c586af9b8c2f0eb07c4a3ed9342f810e2", size = 4659289, upload-time = "2026-04-08T05:10:27.815Z" }, - { url = "https://files.pythonhosted.org/packages/20/3d/27bf74bbb5318ecb224c06697bbbb8bee98b43ab9db9d375b305da11301a/python_fcl-0.7.0.11-cp312-cp312-win_amd64.whl", hash = "sha256:63c662c8ff30eeb78913624a4ac56209a6061248ed97066c3b744255d943299f", size = 1097547, upload-time = "2026-04-08T05:10:29.166Z" }, -] - [[package]] name = "python-lsp-jsonrpc" version = "1.1.2" @@ -8794,15 +8545,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, ] -[[package]] -name = "pyvers" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/62/21/9daf8da2793d112a04b46ac33ab9046c91b0af8292cd93b3cfdd07ff7169/pyvers-0.2.3.tar.gz", hash = "sha256:c4b81c3a033963245e124cdecb052783c9c4cea3bb08c051833af1c44faa6283", size = 12347, upload-time = "2026-07-09T13:58:07.046Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/8d/63787e8feda59d981ebc953b6581ccab3c97cf7bfe0a6a407c07b50fc86c/pyvers-0.2.3-py3-none-any.whl", hash = "sha256:6f5b5612f2f4bd08caa49baa70fc5f875fc7da701a5385c13e244eea6b8114dd", size = 11757, upload-time = "2026-07-09T13:58:06.078Z" }, -] - [[package]] name = "pywin32" version = "311" @@ -9322,36 +9064,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8d/fc/7eedc3510d97878876e32774eebbeb61c43f148a96e915c84229a3e967aa/safetensors-0.8.0-cp310-abi3-win_arm64.whl", hash = "sha256:f7838e5135a406ad3e02efdcb8cf2e5397d368b0154537c4fec682dbc544d452", size = 340500, upload-time = "2026-06-09T07:52:26.745Z" }, ] -[[package]] -name = "scene-synthesizer" -version = "1.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "config-path" }, - { name = "matplotlib" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "python-fcl" }, - { name = "pyyaml" }, - { name = "rtree" }, - { name = "setuptools-scm" }, - { name = "shapely" }, - { name = "triangle" }, - { name = "trimesh" }, - { name = "yourdfpy" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/cb/ccbf1bd9c303cc10153c06fed0dd7455052b146c507018178004c34de747/scene_synthesizer-1.15.0-py3-none-any.whl", hash = "sha256:1323006ff273c4a6d9bd119b7a2cb6ecfe6535c8d576c253d867a9c55cafa247", size = 183496, upload-time = "2025-06-28T01:19:22.322Z" }, -] - -[package.optional-dependencies] -recommend = [ - { name = "dm-control" }, - { name = "usd-core" }, -] - [[package]] name = "scikit-learn" version = "1.7.2" @@ -9365,15 +9077,14 @@ resolution-markers = [ "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version < '3.11' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform != 'darwin' and sys_platform != 'win32'", "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'darwin'", - "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'win32'", "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", ] dependencies = [ - { name = "joblib", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "threadpoolctl", marker = "python_full_version < '3.11'" }, + { name = "joblib", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, + { name = "threadpoolctl", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } wheels = [ @@ -9404,7 +9115,6 @@ resolution-markers = [ "python_full_version >= '3.12' and platform_machine == 'x86_64' and sys_platform == 'linux'", "(python_full_version >= '3.12' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.12' and platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')", "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'darwin'", - "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'", "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'win32'", "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'darwin'", @@ -9412,15 +9122,14 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'linux'", "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')", "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'darwin'", - "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'win32'", "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", ] dependencies = [ - { name = "joblib", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "threadpoolctl", marker = "python_full_version >= '3.11'" }, + { name = "joblib", marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, + { name = "threadpoolctl", marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } wheels = [ @@ -9639,22 +9348,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a9/07/99f2cefae815c66eb23148f15d79ec055429c38fa8986edcc712ab5f3223/setuptools-77.0.3-py3-none-any.whl", hash = "sha256:67122e78221da5cf550ddd04cf8742c8fe12094483749a792d56cd669d6cf58c", size = 1255678, upload-time = "2025-03-20T14:38:06.621Z" }, ] -[[package]] -name = "setuptools-scm" -version = "10.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, - { name = "setuptools" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, - { name = "vcs-versioning" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5d/b1/d0b97ffd2856a7d19c63024a89fb84813cb9d2ed7fa8fdbedf9e2f13a9ab/setuptools_scm-10.2.1.tar.gz", hash = "sha256:4fa7dd82cf8c800df59c9a288c90299b1657ff1ecfc3f5cc00287c5dbf5e27a9", size = 154237, upload-time = "2026-07-21T08:08:04.553Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/b4/02ac1d9833b882c87b3a4e82703e70eac4ab33d0d15fb123e60bb01f3bc1/setuptools_scm-10.2.1-py3-none-any.whl", hash = "sha256:b7c82f4102d389ee57dc66ccdb4f9b4bca3c40ba83b43f1f63d68ccd72db2580", size = 29078, upload-time = "2026-07-21T08:08:03.293Z" }, -] - [[package]] name = "shapely" version = "2.1.2" @@ -9691,16 +9384,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/bf/cb6c1c505cb31e818e900b9312d514f381fbfa5c4363edfce0fcc4f8c1a4/shapely-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:743044b4cfb34f9a67205cee9279feaf60ba7d02e69febc2afc609047cb49179", size = 1722861, upload-time = "2025-09-24T13:50:43.35Z" }, ] -[[package]] -name = "sharedarray" -version = "3.2.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a6/d2/6818d35a7abba9b2410813f2160630e125b12a52ca11acfd1fb0959433ad/SharedArray-3.2.4.tar.gz", hash = "sha256:b8b8d189110c023b9de502f9396ff2591f660fb2c9637eb13fcaf233127e50be", size = 19584, upload-time = "2024-07-18T10:10:53.084Z" } - [[package]] name = "shellingham" version = "1.5.4" @@ -9959,6 +9642,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a8/45/a132b9074aa18e799b891b91ad72133c98d8042c70f6240e4c5f9dabee2f/structlog-25.5.0-py3-none-any.whl", hash = "sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f", size = 72510, upload-time = "2025-10-27T08:28:21.535Z" }, ] +[[package]] +name = "svg-path" +version = "7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/4c/561a320906f9e4ccb7ed9ef18885e8a1afccc7813080a6dbd3e38bd16403/svg_path-7.1.tar.gz", hash = "sha256:1cd254c974dc96cbee3a0cfc6528356866356b712f241b4e9066043ab8703d9b", size = 24175, upload-time = "2026-07-07T18:29:52.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/a1/219769f90ba167b87b5bc4566bafddd2dba05ce55aa82430fe12aaa2b782/svg_path-7.1-py2.py3-none-any.whl", hash = "sha256:2c4afb1457da48ff3b3ef802aacf50fd0f23f99e82b77c6f60abc0f1ddb9c312", size = 18633, upload-time = "2026-07-07T18:29:50.856Z" }, +] + [[package]] name = "sympy" version = "1.13.1" @@ -10069,36 +9761,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/1d/b5d63f1a6b824282b57f7b581810d20b7a28ca951f2d5b59f1eb0782c12b/tensorboardx-2.6.4-py3-none-any.whl", hash = "sha256:5970cf3a1f0a6a6e8b180ccf46f3fe832b8a25a70b86e5a237048a7c0beb18e2", size = 87201, upload-time = "2025-06-10T22:37:05.44Z" }, ] -[[package]] -name = "tensordict" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpickle" }, - { name = "importlib-metadata" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "orjson" }, - { name = "packaging" }, - { name = "pyvers" }, - { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'x86_64' and sys_platform == 'linux') or (sys_platform != 'linux' and sys_platform != 'win32')" }, - { name = "torch", version = "2.7.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or sys_platform == 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/65/81c5bfd5e410e908f183eb683c5d6fe284b98d3f6fd961f77065adb0f632/tensordict-0.13.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:5e76410e72525c47f668324d377c612ae75dcfb089555d529b88a92ece1bbd76", size = 908794, upload-time = "2026-06-04T14:46:45.983Z" }, - { url = "https://files.pythonhosted.org/packages/2f/52/b662ee78687cd127cb7cbab609227266584d30305653647098782f26653e/tensordict-0.13.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6538f8c2760d7f02223090f6e549d61ac7a3de39f413020aed9867e8529195d2", size = 553424, upload-time = "2026-06-04T14:46:47.762Z" }, - { url = "https://files.pythonhosted.org/packages/2d/7b/70fde071680a281a9de3b48c9fe879a2c4c1feabecf2dfe982ecbc9048a1/tensordict-0.13.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:405a2d65e1b8e070c6373a3ce1186852b7ec4c4fa798503d79fb603d143483ec", size = 557986, upload-time = "2026-06-04T14:46:49.307Z" }, - { url = "https://files.pythonhosted.org/packages/dc/95/49c903670d11a1cfaef8e8f12a9c59e692cfad187f4ebe9d0dff92b06dd1/tensordict-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:e0983899f34096627bd5e2d6534bd4fd3ce1028dd8debe789a89f4e5b054e51e", size = 604764, upload-time = "2026-06-04T14:46:50.595Z" }, - { url = "https://files.pythonhosted.org/packages/21/7e/dbe7b63268e2dc1116f44f4bfa083eb28e7f1e264977c3503dd86a6802a1/tensordict-0.13.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:716a4b1b18e481566c0d0994a5a0afd02093600d41cefe7a221c9d0b92518741", size = 910806, upload-time = "2026-06-04T14:46:52.088Z" }, - { url = "https://files.pythonhosted.org/packages/30/de/3b12d62f7b4e654d5a0cc7a1f5f0a1884dc6adb2e6151d5f0bbf9222c11c/tensordict-0.13.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3181e0faf78f192e71aa35f1ab7b88f48714c3aba60198c7a7b8cefb18d7096e", size = 554682, upload-time = "2026-06-04T14:46:53.558Z" }, - { url = "https://files.pythonhosted.org/packages/7a/5a/953199b7dcae3409cef269abadfd90ff26798c3b03d7e291d2c0b505f76c/tensordict-0.13.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:be6b34404788ac6239ad347fb3e16222db79f716a4c3e43e5df378cdaeab4785", size = 559165, upload-time = "2026-06-04T14:46:54.954Z" }, - { url = "https://files.pythonhosted.org/packages/97/5f/dbd18a144034d2e9d8c2b5dbc1d4d2b463fb6b02d5e3adb84333f1fc3117/tensordict-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:36a086188dbb11bcd80cf38e08101e788a59105eb10ae6492109d0c1797b8ee0", size = 607345, upload-time = "2026-06-04T14:46:56.822Z" }, - { url = "https://files.pythonhosted.org/packages/b8/77/6edc42425c4cc3755e52df25478f9b2ab7f67063e0bdc6c472a3db783f0a/tensordict-0.13.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:023d19f36b0aef990668033ca9dd721bace95266d0b9650626bdb4e3f2192f13", size = 911682, upload-time = "2026-06-04T14:46:58.304Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b5/0c3889dabb6d92373036b9c2cc8269aa2fa5c3089f4ef1c04ae34d82b494/tensordict-0.13.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:25dbc418dbfe2733b34467c478f70c1d83870c76029397f00b5c45832d47929a", size = 555486, upload-time = "2026-06-04T14:46:59.802Z" }, - { url = "https://files.pythonhosted.org/packages/89/6c/92da62aefa186e2ab0e307dceccfbcdcb8769ec812a00676cb164ef6f47e/tensordict-0.13.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:343cf19838f6cb6be9db28d164338a399c269171854a6d688cb42f82be796e1c", size = 559537, upload-time = "2026-06-04T14:47:01.096Z" }, - { url = "https://files.pythonhosted.org/packages/48/33/fdbc52ec5069d64910c8efbf64a67fe399134ec9f602d4a3cfa5dd8e5362/tensordict-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:53f60f72554b49d2143768c92396b8af020ab920d6068a46c115b11669444e9b", size = 608643, upload-time = "2026-06-04T14:47:02.466Z" }, -] - [[package]] name = "tensorstore" version = "0.1.78" @@ -10444,27 +10106,6 @@ wheels = [ { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.7.1%2Bcu128-cp312-cp312-win_amd64.whl", hash = "sha256:2bb8c05d48ba815b316879a18195d53a6472a03e297d971e916753f8e1053d30", upload-time = "2025-06-03T18:31:46Z" }, ] -[[package]] -name = "torch-geometric" -version = "2.8.0.post1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "psutil" }, - { name = "pyparsing" }, - { name = "requests" }, - { name = "tqdm" }, - { name = "xxhash" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/40/2c/bfcb404c448f7c347cea7b956d3c7e92d53e36c8db2010eb1bdc1b937bfb/torch_geometric-2.8.0.post1.tar.gz", hash = "sha256:2c0c81666ec10f2132f6b4e0b6c57fc813f2c9f6b57599d7b7a799c614c22fbd", size = 928666, upload-time = "2026-07-20T20:44:40.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/5c/edf74a71249ad19aa0390fb97ff021e2a5b04ce23252730014e1feac957e/torch_geometric-2.8.0.post1-py3-none-any.whl", hash = "sha256:5d9841cbfa64eadc425e445767127d103dd52fdc5890548c6364986c9bc78029", size = 1325397, upload-time = "2026-07-20T20:44:38.789Z" }, -] - [[package]] name = "torchreid" version = "0.2.5" @@ -10627,32 +10268,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/2b/36e984399089c026a6499ac8f7401d38487cf0183839a4aa78140d373771/treescope-0.1.10-py3-none-any.whl", hash = "sha256:dde52f5314f4c29d22157a6fe4d3bd103f9cae02791c9e672eefa32c9aa1da51", size = 182255, upload-time = "2025-08-08T05:43:46.673Z" }, ] -[[package]] -name = "triangle" -version = "20250106" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/55/44/c5b03ff3d806ea05f58e072919c1b002513fab3db17125e14ad70687edd9/triangle-20250106-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5eb99ec1002f164b7a40bec47c9915818231c3563f3900d973b6a2cb2befef9d", size = 1438057, upload-time = "2025-01-07T04:53:58.112Z" }, - { url = "https://files.pythonhosted.org/packages/ef/8f/8585f9fa048ca4541e1b97562bd5518731af73a84739e852c27a9c58478f/triangle-20250106-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d32c36a7188234a225a41f98224e430eae48ba252942696f2a7c83693a41fd0f", size = 2059234, upload-time = "2025-01-07T04:55:58.791Z" }, - { url = "https://files.pythonhosted.org/packages/ed/f2/a5e0882f3a65f256f925b3034e0dfa1a40a6b9b70fb52cd2d04507b372ba/triangle-20250106-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e08357d66a48d8742d25479f55478911750513f4d3875e836d5d6cc0ba9dbda1", size = 1985587, upload-time = "2025-01-07T04:56:01.34Z" }, - { url = "https://files.pythonhosted.org/packages/45/ac/cad720cd3bbdd30c91529556ca1616fc34d4b94e9cd50bff15d9dd7bad2f/triangle-20250106-cp310-cp310-win32.whl", hash = "sha256:11a0b634af43789e4525f2f4247dda83043f9fdfd710b55bcb82b514471ab6d7", size = 1406838, upload-time = "2025-01-07T05:00:05.181Z" }, - { url = "https://files.pythonhosted.org/packages/e6/82/01827a6c872f7588f4252c05a542764f561c7cadcd564a4000e8afd2d33f/triangle-20250106-cp310-cp310-win_amd64.whl", hash = "sha256:8a31f67e3506cc3aa33f82f1f94ea365b9625a7737fa119d628580c8bac0b0bf", size = 1425912, upload-time = "2025-01-07T05:00:07.885Z" }, - { url = "https://files.pythonhosted.org/packages/5c/91/83167e3d0cd1912b46e61a2b3485bf9886e49b48cea4daa59e8de4058a1a/triangle-20250106-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93bd02b3341b2e4452952cb7605cf239bddd788a6f410ea54aeeb7db61836ba5", size = 1438115, upload-time = "2025-01-07T04:54:01.302Z" }, - { url = "https://files.pythonhosted.org/packages/8b/de/2383f2cd1dcc93a7042fb4feedb551c29cc40a3dffdc00b0387a2c591456/triangle-20250106-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80da149e4584cc599fc9bcff31dddd524528a41119f5af321911e599d82ad6e1", size = 2100052, upload-time = "2025-01-07T04:56:03.954Z" }, - { url = "https://files.pythonhosted.org/packages/ae/d9/30504df67bb5451097ad4cb67f558be41ed7bf780f300bae1fc7cb90ee05/triangle-20250106-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1a22e82701fe97c0cbfe9ecbfc394a4a36442f1fe2836a721ddb95be9cdb299", size = 2028993, upload-time = "2025-01-07T04:56:06.767Z" }, - { url = "https://files.pythonhosted.org/packages/f7/4b/b8b6e97452bbadb9d388a132c78b0b4e1eb9e61ffc7c3c8b4992342327ed/triangle-20250106-cp311-cp311-win32.whl", hash = "sha256:3097f96859c02c9e3f5a2c35b0016b28005e7e6f061059b9c7a412e2ad01eb86", size = 1406419, upload-time = "2025-01-07T05:00:10.279Z" }, - { url = "https://files.pythonhosted.org/packages/0d/18/2a5fe89c7b98501d1dbb267119a3ce993ab96f6f11abf9e879dbbe3297ac/triangle-20250106-cp311-cp311-win_amd64.whl", hash = "sha256:cbd195668be437cfdc8cf192cec5748ebbb5d3bff19201f8503495916dd54ded", size = 1425906, upload-time = "2025-01-07T05:00:12.201Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ba/22c552b21aa5a7724e712372d29c9397db19086e99c62f876c1b73025df2/triangle-20250106-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64106544f6d137b619f7f724abbb7c2c78353691cccfc163e9d0b2e2476e0853", size = 1439851, upload-time = "2025-01-07T04:54:03.857Z" }, - { url = "https://files.pythonhosted.org/packages/fa/93/ce4d0c46ff570993f4302ce55300dd310b7c957a8e66890ed00691229f5b/triangle-20250106-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38c221bb6e403f81de49050899502a8326c9565f62c4d69171070b41dc25cb69", size = 2089547, upload-time = "2025-01-07T04:56:09.463Z" }, - { url = "https://files.pythonhosted.org/packages/23/e0/bd0a7e624fc5fc8636d0ad281c5b0624027dc1855218ce6a251c581d7127/triangle-20250106-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b582b342cddb660dffc6bd7d7233f66952d2a3d18ccbc097660a8e370ee38d0c", size = 2009167, upload-time = "2025-01-07T04:56:12.221Z" }, - { url = "https://files.pythonhosted.org/packages/26/3f/7c79202ec374bd122b63250d768be34674043be9b97f6bb8c115df64e880/triangle-20250106-cp312-cp312-win32.whl", hash = "sha256:9532797a15687225a0ee67619ad6f3baeb72bf193541eb96f782e41c577cc81b", size = 1407116, upload-time = "2025-01-07T05:00:13.664Z" }, - { url = "https://files.pythonhosted.org/packages/a1/a5/4a09c3f9d2687d8752c912a97f2c5086cdd83721b3b13f8288f13b771fa7/triangle-20250106-cp312-cp312-win_amd64.whl", hash = "sha256:0327032a7984a7262180ef2ddd78b36dfdcdecbc79f0f9f173732ce7c670b8ed", size = 1426720, upload-time = "2025-01-07T05:00:17.789Z" }, -] - [[package]] name = "trimesh" version = "4.12.2" @@ -10666,6 +10281,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/05/98/716a473cfb24750858ddd5d14e6527539dd206583a46408d08eeb2844a75/trimesh-4.12.2-py3-none-any.whl", hash = "sha256:b5b5afa63c5272345f2858f7676bc8c217dc8a89f4fadf6193fe10a81b5ff2aa", size = 741043, upload-time = "2026-05-01T00:57:40.763Z" }, ] +[package.optional-dependencies] +easy = [ + { name = "charset-normalizer" }, + { name = "colorlog" }, + { name = "embreex" }, + { name = "httpx" }, + { name = "jsonschema" }, + { name = "lxml" }, + { name = "manifold3d" }, + { name = "mapbox-earcut" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pillow" }, + { name = "pycollada" }, + { name = "rtree" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "shapely" }, + { name = "svg-path" }, + { name = "vhacdx" }, + { name = "xxhash" }, +] + [[package]] name = "triton" version = "3.2.0" @@ -10952,24 +10590,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/09/ef/697ccbf91833155fa36ee0919918eb02df49a47005d1ff8a78ca13e09893/unitree_webrtc_connect-2.1.2-py3-none-any.whl", hash = "sha256:230bcbc5cc39f0dd621cdebf0568e96274c3531e232611293f8182af9e827d80", size = 52315, upload-time = "2026-05-17T22:06:40.924Z" }, ] -[[package]] -name = "urdfpy" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lxml" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pillow" }, - { name = "pycollada" }, - { name = "pyrender" }, - { name = "six" }, - { name = "trimesh" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/8d/3e64966a55afe0214f2f006730dad7c615828117b0c56de0b8819c6575bc/urdfpy-0.0.4.tar.gz", hash = "sha256:5bae6e06572b72426565bcca2106de712a18cfb91a6b00c159451263e72a5839", size = 20777, upload-time = "2019-03-03T23:23:04.779Z" } - [[package]] name = "urllib3" version = "2.6.3" @@ -11076,17 +10696,36 @@ wheels = [ ] [[package]] -name = "vcs-versioning" -version = "2.2.2" +name = "vhacdx" +version = "0.0.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "packaging" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/95/c95bb74950763a163defcf4cedf6c5edfca1d623fd5031b76516ece85076/vcs_versioning-2.2.2.tar.gz", hash = "sha256:4ac4ded78720cdb4d0291ae58ace87e1e9201912e1023f3029c6cce5c9152cfb", size = 143135, upload-time = "2026-06-29T13:26:06.901Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/9e/1f06f4ddc3a74ccfe0877490caaf4a5233e65401160afc8cb13207815f5e/vcs_versioning-2.2.2-py3-none-any.whl", hash = "sha256:fe7fb216f8780a5516e7864a9333aacb1b70015b087a9be3918da76ef2760808", size = 108014, upload-time = "2026-06-29T13:26:05.367Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/15/e3/d2abc3dc4c1cb216c2efdc70b36f80efeb1bdbd7d420a676ddc9d9d980e1/vhacdx-0.0.10.tar.gz", hash = "sha256:fcc23201e319d79fe25e064847efc254bd39ac30af28cc761409e1f9142dd033", size = 58125, upload-time = "2025-12-02T20:58:45.358Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/f4/da308d86daaa9c636851357cbd928715d47963beecd525b3749d2d5c9537/vhacdx-0.0.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc7be82fab608cb7231e95a0a10700be1e9422a36b21e7d49c782a598c8d37c", size = 222760, upload-time = "2025-12-02T20:57:30.778Z" }, + { url = "https://files.pythonhosted.org/packages/e0/8a/e3462a43ec6712b74d921e4af9d5a2998752378c5554bde9a594dbb0cf0c/vhacdx-0.0.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4b63d1c5ad0e64c300a3a9d9404f4778df367b8c545639dfb932db4b76704ff3", size = 208812, upload-time = "2025-12-02T20:57:33.486Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d1/b717275adb108431f1404193542fab7ecf4c5bae221f1552bbd570fe0e5d/vhacdx-0.0.10-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9bcf3fe1c555598e41348108b55a0fc67534e7fef2367452c301014518c1476", size = 236999, upload-time = "2025-12-02T20:57:34.971Z" }, + { url = "https://files.pythonhosted.org/packages/bf/84/97e2305f6bd4a4de3d40bb234c38282cbcf2fa30653ff5ae4f7df9d8f3ec/vhacdx-0.0.10-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9506ca89289da63e5a3d1ac97aa7413aece47d65cbaa4b0c409469555add0e06", size = 250035, upload-time = "2025-12-02T20:57:36.037Z" }, + { url = "https://files.pythonhosted.org/packages/9d/66/eb1d8d64742b9e73557e075cea6ee7e4976dd89b84c7d3197ca3621d5a85/vhacdx-0.0.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06faf9caa0abceddd5fa505e4299e2ebf14bc26c58a1e521013717cbf37bea61", size = 1224134, upload-time = "2025-12-02T20:57:37.217Z" }, + { url = "https://files.pythonhosted.org/packages/47/db/e829b21b071db94f45079c4ace2f967c684f08b10ea285919a95e9d5fe21/vhacdx-0.0.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3a6b43b42290697e2bd04087977d1e3841d287c528e414c765581ecec62e66f6", size = 1284300, upload-time = "2025-12-02T20:57:38.78Z" }, + { url = "https://files.pythonhosted.org/packages/ff/aa/b401565542b927ce3e0a6d5e72acef79343a449ee1a7ad94a5c7266bab26/vhacdx-0.0.10-cp310-cp310-win_amd64.whl", hash = "sha256:27eb3b293ccef1332d477346d564bb4c474bb451e9b753e3ce9cac01cbb90a0c", size = 193069, upload-time = "2025-12-02T20:57:40.318Z" }, + { url = "https://files.pythonhosted.org/packages/b7/2c/d49df6fec3294cef3c8c88c54784162bd8350c427fecd9b16335772b760f/vhacdx-0.0.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8584f33ed020b6cce678b8febcf84af22bced617ef31c85bf31fd7e2b4bba9fe", size = 224113, upload-time = "2025-12-02T20:57:41.59Z" }, + { url = "https://files.pythonhosted.org/packages/68/1d/bd2456baa6b16977c106adc2386b6e7a34c3e57ade6aeeab68bb61ceb16f/vhacdx-0.0.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a9b63cdb5f34dfee386b64a01f7e1571ef0c2555244ea3d83a09d78273123bce", size = 210118, upload-time = "2025-12-02T20:57:42.749Z" }, + { url = "https://files.pythonhosted.org/packages/49/ab/15adb78489b51c2a898642755be727ecd7c3de37cac6e434ce420b8ce27c/vhacdx-0.0.10-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:915eab6c19fdf63ab256855331db546575284786a480aa2d67437db0e86b0d17", size = 238276, upload-time = "2025-12-02T20:57:43.95Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f1/464c761dbe24f58d6fc354bf51729342981fb7a621e170e0d3512fadbec8/vhacdx-0.0.10-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e335bb9af540e6867ff051166a399075823fdd8fc1fc27e9530995cc1bda1eb", size = 251383, upload-time = "2025-12-02T20:57:45.246Z" }, + { url = "https://files.pythonhosted.org/packages/b2/22/c7b4117c5431189a6a019e8fc2cf590df3ab196c38b4b7c3622292205d9b/vhacdx-0.0.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c3ddbaa38eb65c3aec9b0e39a822223474c931c0e18d3e93a3a499870ffa45ad", size = 1225200, upload-time = "2025-12-02T20:57:46.639Z" }, + { url = "https://files.pythonhosted.org/packages/6c/62/c679ad28ce7854771913255e1abc588b3643c2147fb5c51a8553224aa1dd/vhacdx-0.0.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d398fcc13e330ed1fd2540a7d572aeca0be9621411def78e10c7ea4132f959ee", size = 1285935, upload-time = "2025-12-02T20:57:48.51Z" }, + { url = "https://files.pythonhosted.org/packages/de/c8/a8260b780e4578d7ef19b70343f9717f74ff48f9950138c96c78f209ec01/vhacdx-0.0.10-cp311-cp311-win_amd64.whl", hash = "sha256:c9665a3ef887babcac8b5822f01288e8f06b4a949fadbbe1861670b358f111ee", size = 194137, upload-time = "2025-12-02T20:57:50.207Z" }, + { url = "https://files.pythonhosted.org/packages/cf/9c/66375e65634c80f6efb46e81915126bf3e55dc9d6615217590cbc8316d2e/vhacdx-0.0.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7dd17d697d6d4d7cf66f1e947e0530041913981e05f7025236bec28a350b1a33", size = 224998, upload-time = "2025-12-02T20:57:51.639Z" }, + { url = "https://files.pythonhosted.org/packages/4e/e3/fc2644d3e7d0b2b52e2f681eb2878c0e1b9cafc53946f66736d0f01e237c/vhacdx-0.0.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:189ded39b709436cb732cdf694d4cf22e877aefb97e2ab2b55bf7ada9c030f93", size = 211130, upload-time = "2025-12-02T20:57:53.018Z" }, + { url = "https://files.pythonhosted.org/packages/e3/93/0b0f1977f5b3c2e1bbea5ef85e37a808ff73f1b7daf42950c57090e90dc7/vhacdx-0.0.10-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3b03d35ab56a93beee338175dbe0b87552353e5dfb3ff37467e88f56cedf7cc", size = 239661, upload-time = "2025-12-02T20:57:54.144Z" }, + { url = "https://files.pythonhosted.org/packages/94/98/d2a6aeb1c6570a1fc1be29ee03db795f643ab03c6df7635522f23796b39d/vhacdx-0.0.10-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea8c54ed193fa0db0248928fbf5d438b3872d615a506889d5b89fc6467d6411a", size = 252938, upload-time = "2025-12-02T20:57:55.275Z" }, + { url = "https://files.pythonhosted.org/packages/94/2e/1e678efc161a0d7fe1806f5e037ce11cc5964db7e08ccfc220ef63951863/vhacdx-0.0.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a5c898104140c72e4dc789e6125812671eee5e412916e83eff24a6148248ff5e", size = 1226696, upload-time = "2025-12-02T20:57:56.438Z" }, + { url = "https://files.pythonhosted.org/packages/90/5b/b302a0420a241c4910f4870eb9f39e6ada59858db441cc35bda511c17982/vhacdx-0.0.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abdd0ba17786e578206594731df15c90e2751b6884220c8673124f47fd7ac620", size = 1287794, upload-time = "2025-12-02T20:57:57.694Z" }, + { url = "https://files.pythonhosted.org/packages/73/e9/f9729603ac75047a257f1b4ddac60cbde72b0abfd49ffed305751ba630a2/vhacdx-0.0.10-cp312-cp312-win_amd64.whl", hash = "sha256:79e7db59b4042295b21b79d55ba486a9a480550f696d466f158a30ed920dd0ec", size = 195033, upload-time = "2025-12-02T20:57:58.95Z" }, ] [[package]] @@ -11281,21 +10920,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, ] -[[package]] -name = "webdataset" -version = "1.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "braceexpand" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/3a/68800d92e065cf4750ebecf973b13979c0c929b439e1293012938862038d/webdataset-1.0.2.tar.gz", hash = "sha256:7f0498be827cfa46cc5430a58768a24e2c6a410676a61be1838f53d61afdaab4", size = 80090, upload-time = "2025-06-19T23:26:21.945Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/00/aca6beb3658dab4ed3dbb41a78e6e7f31342e0b41d28088f205525751601/webdataset-1.0.2-py3-none-any.whl", hash = "sha256:3dbfced32b25c0d199c6b9787937b6f85742bc3c84f652c846893075c1c082d9", size = 74956, upload-time = "2025-06-19T23:26:20.354Z" }, -] - [[package]] name = "websocket-client" version = "1.9.0" @@ -11766,7 +11390,7 @@ dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "six" }, - { name = "trimesh" }, + { name = "trimesh", extra = ["easy"] }, ] sdist = { url = "https://files.pythonhosted.org/packages/ff/19/20c50861f30aff7720f9a601f386d73760c2df9961de1f98d0dbf3b85e69/yourdfpy-0.0.60.tar.gz", hash = "sha256:2af2d8bdeea1b85b642590a3b4236fdb35746d7b3e38ce460a169c18d9c4f868", size = 538238, upload-time = "2026-01-23T07:32:47.856Z" } wheels = [ From 77d27ba6f2deb715b3693104c01136799c9f3063 Mon Sep 17 00:00:00 2001 From: cc Date: Sat, 12 Sep 2026 16:45:46 -0700 Subject: [PATCH 02/12] refactor(grasping): respect isolated runtime project boundaries --- .github/workflows/ci.yml | 9 + dimos/conftest.py | 7 + dimos/experimental/isolated_python/README.md | 7 + .../grasping/grasp_gen_x/module.py | 2 +- .../python/graspgenx_runtime/runtime.py | 2 +- .../python/graspgenx_runtime/test_runtime.py | 212 ++++++++++++++++++ .../grasp_gen_x/python/pyproject.toml | 8 + .../grasping/grasp_gen_x/python/uv.lock | 45 +++- .../grasping/grasp_gen_x/test_module.py | 201 ++--------------- dimos/robot/test_all_blueprints_generation.py | 43 +++- docs/capabilities/manipulation/xarm-grasp.md | 20 +- pyproject.toml | 1 + 12 files changed, 363 insertions(+), 194 deletions(-) create mode 100644 dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/test_runtime.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 18312487a3..0d211a5b99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -667,6 +667,15 @@ jobs: run: uv sync --group tests --frozen - name: Run tests run: uv run pytest --numprocesses=logical --cov=dimos/ --junitxml=junit.xml -m 'not (self_hosted or mujoco or self_hosted_large or web_browser or bake_e2e or native_e2e)' + - name: Run isolated GraspGenX runtime tests + if: runner.os == 'Linux' && runner.arch == 'X64' && matrix.pyver == '3.12' + working-directory: dimos/manipulation/grasping/grasp_gen_x/python + env: + UV_PROJECT_ENVIRONMENT: ${{ runner.temp }}/graspgenx-tests + run: >- + uv run --frozen --group tests --with-editable "$GITHUB_WORKSPACE" + python -m pytest -c pyproject.toml --confcutdir=. + --junitxml="$GITHUB_WORKSPACE/junit-graspgenx.xml" - name: Re-run the failing tests with maximum verbosity if: failure() env: diff --git a/dimos/conftest.py b/dimos/conftest.py index a29d5d2b73..01fab14a26 100644 --- a/dimos/conftest.py +++ b/dimos/conftest.py @@ -167,6 +167,13 @@ def pytest_sessionstart(session): _arm_crash_dumps() +def pytest_ignore_collect(collection_path: pathlib.Path) -> bool | None: + # Nested Python projects own their dependencies and test invocation. + if collection_path.is_dir() and (collection_path / "pyproject.toml").is_file(): + return True + return None + + def pytest_configure(config): config.addinivalue_line( "markers", diff --git a/dimos/experimental/isolated_python/README.md b/dimos/experimental/isolated_python/README.md index 80f4a37c9a..97cb646d6c 100644 --- a/dimos/experimental/isolated_python/README.md +++ b/dimos/experimental/isolated_python/README.md @@ -90,6 +90,13 @@ values, exceptions, timeouts, async methods, skills, streams, and module references. Restarting the contract starts a fresh interpreter and reloads the runtime package. +Nested Python projects own their runtime classes and tests. Host blueprint discovery +and pytest collection stop at directories containing another `pyproject.toml`. +Runtime classes can use public names without becoming host registry entries. +Run runtime tests explicitly with their project's pytest configuration and +`--confcutdir=.` to avoid loading the host's test fixtures. Keep test environments +outside `dimos/`, where repository source checks would otherwise scan dependencies. + ## Example The source tree includes a complete example with a locked external project: diff --git a/dimos/manipulation/grasping/grasp_gen_x/module.py b/dimos/manipulation/grasping/grasp_gen_x/module.py index 4ecfd818a9..b10cd66046 100644 --- a/dimos/manipulation/grasping/grasp_gen_x/module.py +++ b/dimos/manipulation/grasping/grasp_gen_x/module.py @@ -92,7 +92,7 @@ class GraspGenXError(RuntimeError): class GraspGenXModule(IsolatedPythonModule, GraspGenSpec): """Grasp proposals implemented in a separately locked Python environment.""" - implementation = "graspgenx_runtime.runtime:_GraspGenXRuntimeModule" + implementation = "graspgenx_runtime.runtime:GraspGenXRuntimeModule" config: GraspGenXConfig @rpc diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/runtime.py b/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/runtime.py index 0946c4e799..e5d672b44a 100644 --- a/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/runtime.py +++ b/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/runtime.py @@ -45,7 +45,7 @@ def _create_runtime(config: GraspGenXConfig) -> GraspGenXRuntime: return GraspGenXRuntime(config) -class _GraspGenXRuntimeModule(GraspGenXModule): +class GraspGenXRuntimeModule(GraspGenXModule): """Direct adapter whose optional runtime is loaded synchronously by ``start``.""" def __init__(self, **kwargs: Any) -> None: diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/test_runtime.py b/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/test_runtime.py new file mode 100644 index 0000000000..faa7ea6824 --- /dev/null +++ b/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/test_runtime.py @@ -0,0 +1,212 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Proposal behavior tests run in the isolated project's environment.""" + +from __future__ import annotations + +import inspect +from typing import Any + +import graspgenx_runtime.runtime as runtime_module +from graspgenx_runtime.runtime import GraspGenXRuntimeModule +import numpy as np +import pytest +from pytest_mock import MockerFixture + +from dimos.experimental.isolated_python.bootstrap import validate_runtime +from dimos.manipulation.grasping.grasp_gen_x.module import ( + GraspGenXConfig, + GraspGenXError, + GraspGenXModule, +) +from dimos.msgs.sensor_msgs.PointCloud2 import PointCloud2 + + +def config(**overrides: Any) -> GraspGenXConfig: + values: dict[str, Any] = { + "gripper": { + "extents_open": (0.1, 0.1, 0.1), + "offset_open": (0.0, 0.0, 0.0), + "extents_half_open": (0.1, 0.1, 0.1), + "offset_half_open": (0.0, 0.0, 0.0), + "fingertip_depth": 0.1, + }, + } + values.update(overrides) + return GraspGenXConfig(**values) # type: ignore[arg-type] + + +def module_args(value: GraspGenXConfig | None = None) -> dict[str, Any]: + return (value or config()).model_dump(exclude={"rpc_transport", "tf_transport", "g"}) + + +def cloud(points: np.ndarray | None = None) -> PointCloud2: + xyz = np.zeros((1, 3), dtype=np.float32) if points is None else points + return PointCloud2.from_numpy(xyz, frame_id="camera", timestamp=12.5) + + +@pytest.fixture +def runtime(mocker: MockerFixture) -> Any: + create_runtime = mocker.patch.object(runtime_module, "_create_runtime") + instance = create_runtime.return_value + instance.infer.return_value = ( + np.repeat(np.eye(4, dtype=np.float32)[None], 1, axis=0), + np.asarray([0.5], dtype=np.float32), + ) + return create_runtime + + +def test_start_is_synchronous_and_idempotent(runtime: Any) -> None: + module = GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) + try: + module.start() + module.start() + + runtime.assert_called_once_with(module.config) + assert len(module.propose_grasps(cloud())) == 1 + finally: + module.stop() + + +def test_start_failure_is_explicit(runtime: Any) -> None: + runtime.side_effect = RuntimeError("CUDA unavailable") + module = GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) + try: + with pytest.raises(GraspGenXError, match="initialize"): + module.start() + finally: + module.stop() + + +def test_adapter_sorts_stably_truncates_and_applies_tcp_transform(runtime: Any) -> None: + poses = np.repeat(np.eye(4, dtype=np.float32)[None], 3, axis=0) + poses[:, 0, 3] = [1.0, 2.0, 3.0] + runtime.return_value.infer.return_value = ( + poses, + np.asarray([0.5, 0.5, 0.9], dtype=np.float32), + ) + cfg = config( + max_candidates=2, + grasp_frame_to_tcp=( + (0.0, -1.0, 0.0, 10.0), + (1.0, 0.0, 0.0, 0.0), + (0.0, 0.0, 1.0, 0.0), + (0.0, 0.0, 0.0, 1.0), + ), + ) + module = GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args(cfg)) + try: + module.start() + result = module.propose_grasps(cloud()) + + assert [candidate.score for candidate in result] == pytest.approx([0.9, 0.5]) + assert [candidate.pose.position.x for candidate in result] == pytest.approx([13.0, 11.0]) + assert result.header.frame_id == "camera" + assert result.header.timestamp == pytest.approx(12.5) + finally: + module.stop() + + +def test_empty_backend_result_preserves_input_header(runtime: Any) -> None: + runtime.return_value.infer.return_value = ( + np.empty((0, 4, 4), dtype=np.float32), + np.empty(0, dtype=np.float32), + ) + module = GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) + try: + module.start() + result = module.propose_grasps(cloud()) + + assert result.header.frame_id == "camera" + assert result.header.timestamp == pytest.approx(12.5) + assert result.candidates == [] + finally: + module.stop() + + +@pytest.mark.parametrize( + "points", + [ + np.array([[np.nan, 0.0, 0.0]], dtype=np.float32), + np.empty((0, 3), dtype=np.float32), + np.zeros((2, 2), dtype=np.float32), + ], +) +def test_invalid_cloud_points_are_rejected(runtime: Any, points: np.ndarray) -> None: + module = GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) + try: + module.start() + with pytest.raises(ValueError, match="pointcloud|XYZ"): + module.propose_grasps(cloud(points)) + runtime.return_value.infer.assert_not_called() + finally: + module.stop() + + +@pytest.mark.parametrize( + "backend", + [ + (np.ones((2, 4, 4)), np.ones(1)), + (np.full((1, 4, 4), np.nan), np.ones(1)), + (np.ones((1, 4, 4)), np.array([np.inf])), + ], +) +def test_invalid_backend_outputs_are_rejected( + runtime: Any, backend: tuple[np.ndarray, np.ndarray] +) -> None: + runtime.return_value.infer.return_value = backend + module = GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) + try: + module.start() + with pytest.raises(ValueError): + module.propose_grasps(cloud()) + finally: + module.stop() + + +def test_inference_failure_is_wrapped(runtime: Any) -> None: + runtime.return_value.infer.side_effect = RuntimeError("backend") + module = GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) + try: + module.start() + with pytest.raises(GraspGenXError, match="inference"): + module.propose_grasps(cloud()) + finally: + module.stop() + + +def test_not_started_and_missing_metadata_are_rejected(runtime: Any) -> None: + module = GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) + missing_frame = cloud() + missing_frame.frame_id = "" + missing_timestamp = cloud() + missing_timestamp.ts = None + try: + with pytest.raises(GraspGenXError, match="not been started"): + module.propose_grasps(cloud()) + module.start() + with pytest.raises(ValueError, match="frame_id"): + module.propose_grasps(missing_frame) + with pytest.raises(ValueError, match="timestamp"): + module.propose_grasps(missing_timestamp) + finally: + module.stop() + + +def test_runtime_implements_the_host_contract() -> None: + validate_runtime(GraspGenXModule, GraspGenXRuntimeModule) + assert inspect.signature(GraspGenXModule.propose_grasps) == inspect.signature( + GraspGenXRuntimeModule.propose_grasps + ) diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml b/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml index 41d984b60e..ab7443fe87 100644 --- a/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml +++ b/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml @@ -4,7 +4,15 @@ version = "0.1.0" requires-python = ">=3.12,<3.13" dependencies = ["graspgenx", "matplotlib>=3.7.1"] +[dependency-groups] +tests = ["pytest==8.3.5", "pytest-mock==3.15.0", "pytest-timeout==2.4.0"] + +[tool.pytest.ini_options] +testpaths = ["graspgenx_runtime"] +addopts = "--confcutdir=. --import-mode=importlib --timeout=60 -ra" + [tool.uv] +default-groups = [] package = false required-version = ">=0.9.25" environments = ["sys_platform == 'linux' and platform_machine == 'x86_64'"] diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock b/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock index 4ffeddc4dc..4b72189c1c 100644 --- a/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock +++ b/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock @@ -229,12 +229,26 @@ dependencies = [ { name = "matplotlib", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, ] +[package.dev-dependencies] +tests = [ + { name = "pytest", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pytest-mock", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pytest-timeout", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] + [package.metadata] requires-dist = [ { name = "graspgenx", git = "https://github.com/NVlabs/GraspGenX.git?rev=b9429097728cb1c430dd78b92edf17ba318aad03" }, { name = "matplotlib", specifier = ">=3.7.1" }, ] +[package.metadata.requires-dev] +tests = [ + { name = "pytest", specifier = "==8.3.5" }, + { name = "pytest-mock", specifier = "==3.15.0" }, + { name = "pytest-timeout", specifier = "==2.4.0" }, +] + [[package]] name = "dm-control" version = "1.0.46" @@ -1038,17 +1052,40 @@ wheels = [ [[package]] name = "pytest" -version = "9.1.1" +version = "8.3.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "iniconfig", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "pluggy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "pygments", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, +] + +[[package]] +name = "pytest-mock" +version = "3.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/99/3323ee5c16b3637b4d941c362182d3e749c11e400bea31018c42219f3a98/pytest_mock-3.15.0.tar.gz", hash = "sha256:ab896bd190316b9d5d87b277569dfcdf718b2d049a2ccff5f7aca279c002a1cf", size = 33838, upload-time = "2025-09-04T20:57:48.679Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/b3/7fefc43fb706380144bcd293cc6e446e6f637ddfa8b83f48d1734156b529/pytest_mock-3.15.0-py3-none-any.whl", hash = "sha256:ef2219485fb1bd256b00e7ad7466ce26729b30eadfc7cbcdb4fa9a92ca68db6f", size = 10050, upload-time = "2025-09-04T20:57:47.274Z" }, +] + +[[package]] +name = "pytest-timeout" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" }, ] [[package]] diff --git a/dimos/manipulation/grasping/grasp_gen_x/test_module.py b/dimos/manipulation/grasping/grasp_gen_x/test_module.py index 7057ff84b4..ceed6d34d2 100644 --- a/dimos/manipulation/grasping/grasp_gen_x/test_module.py +++ b/dimos/manipulation/grasping/grasp_gen_x/test_module.py @@ -12,39 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Hermetic tests for the import-safe GraspGenX adapter.""" +"""Host contract tests; the isolated runtime is not imported here.""" from __future__ import annotations import inspect +from pathlib import Path import subprocess import sys from typing import Any import numpy as np import pytest -from pytest_mock import MockerFixture -from dimos.experimental.isolated_python.bootstrap import validate_runtime from dimos.manipulation.grasping.grasp_gen_spec import GraspGenSpec, LegacyGraspGenSpec -from dimos.manipulation.grasping.grasp_gen_x.module import ( - GraspGenXConfig, - GraspGenXError, - GraspGenXModule, -) -from dimos.manipulation.grasping.grasp_gen_x.python.graspgenx_runtime import runtime as grasp_gen_x -from dimos.manipulation.grasping.grasp_gen_x.python.graspgenx_runtime.runtime import ( - _GraspGenXRuntimeModule, -) +from dimos.manipulation.grasping.grasp_gen_x.module import GraspGenXConfig from dimos.msgs.geometry_msgs.Pose import Pose from dimos.msgs.manipulation_msgs.GraspCandidate import GraspCandidate from dimos.msgs.manipulation_msgs.GraspCandidateArray import GraspCandidateArray -from dimos.msgs.sensor_msgs.PointCloud2 import PointCloud2 from dimos.msgs.std_msgs.Header import Header -def config(**overrides: object) -> GraspGenXConfig: - values: dict[str, object] = { +def config(**overrides: Any) -> GraspGenXConfig: + values: dict[str, Any] = { "gripper": { "extents_open": (0.1, 0.1, 0.1), "offset_open": (0.0, 0.0, 0.0), @@ -57,15 +47,6 @@ def config(**overrides: object) -> GraspGenXConfig: return GraspGenXConfig(**values) # type: ignore[arg-type] -def module_args(value: GraspGenXConfig | None = None) -> dict[str, Any]: - return (value or config()).model_dump(exclude={"rpc_transport", "tf_transport", "g"}) - - -def cloud(points: np.ndarray | None = None) -> PointCloud2: - xyz = np.zeros((1, 3), dtype=np.float32) if points is None else points - return PointCloud2.from_numpy(xyz, frame_id="camera", timestamp=12.5) - - def test_public_adapter_import_does_not_load_optional_runtime() -> None: result = subprocess.run( [ @@ -85,17 +66,6 @@ def test_public_adapter_import_does_not_load_optional_runtime() -> None: assert result.returncode == 0, result.stderr -@pytest.fixture -def runtime(mocker: MockerFixture) -> Any: - create_runtime = mocker.patch.object(grasp_gen_x, "_create_runtime") - instance = create_runtime.return_value - instance.infer.return_value = ( - np.repeat(np.eye(4, dtype=np.float32)[None], 1, axis=0), - np.asarray([0.5], dtype=np.float32), - ) - return create_runtime - - def test_messages_round_trip_empty_and_score() -> None: value = GraspCandidateArray(Header(3.0, "camera"), [GraspCandidate(Pose(1, 2, 3), 0.25)]) decoded = GraspCandidateArray.decode(value.encode()) @@ -137,7 +107,7 @@ def test_ranked_spec_is_canonical_during_legacy_contract_transition() -> None: ("fingertip_depth", 0.0), ], ) -def test_gripper_constraints_are_declared_by_fields(field: str, value: object) -> None: +def test_gripper_constraints_are_declared_by_fields(field: str, value: Any) -> None: gripper = config().gripper.model_dump() with pytest.raises(ValueError): @@ -145,7 +115,7 @@ def test_gripper_constraints_are_declared_by_fields(field: str, value: object) - @pytest.mark.parametrize("value", [0, -1, True]) -def test_candidate_limit_is_a_strict_positive_integer(value: object) -> None: +def test_candidate_limit_is_a_strict_positive_integer(value: Any) -> None: with pytest.raises(ValueError): config(max_candidates=value) @@ -162,145 +132,22 @@ def test_rigid_transform_relational_validation() -> None: ) -def test_start_is_synchronous_and_idempotent(runtime: Any) -> None: - module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) - try: - module.start() - module.start() - - runtime.assert_called_once_with(module.config) - assert len(module.propose_grasps(cloud())) == 1 - finally: - module.stop() - - -def test_start_failure_is_explicit(runtime: Any) -> None: - runtime.side_effect = RuntimeError("CUDA unavailable") - module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) - try: - with pytest.raises(GraspGenXError, match="initialize"): - module.start() - finally: - module.stop() - - -def test_adapter_sorts_stably_truncates_and_applies_tcp_transform(runtime: Any) -> None: - poses = np.repeat(np.eye(4, dtype=np.float32)[None], 3, axis=0) - poses[:, 0, 3] = [1.0, 2.0, 3.0] - runtime.return_value.infer.return_value = ( - poses, - np.asarray([0.5, 0.5, 0.9], dtype=np.float32), - ) - cfg = config( - max_candidates=2, - grasp_frame_to_tcp=( - (0.0, -1.0, 0.0, 10.0), - (1.0, 0.0, 0.0, 0.0), - (0.0, 0.0, 1.0, 0.0), - (0.0, 0.0, 0.0, 1.0), - ), - ) - module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args(cfg)) - try: - module.start() - result = module.propose_grasps(cloud()) - - assert [candidate.score for candidate in result] == pytest.approx([0.9, 0.5]) - assert [candidate.pose.position.x for candidate in result] == pytest.approx([13.0, 11.0]) - assert result.header.frame_id == "camera" - assert result.header.timestamp == pytest.approx(12.5) - finally: - module.stop() - - -def test_empty_backend_result_preserves_input_header(runtime: Any) -> None: - runtime.return_value.infer.return_value = ( - np.empty((0, 4, 4), dtype=np.float32), - np.empty(0, dtype=np.float32), +def test_host_collection_excludes_the_nested_runtime_suite() -> None: + result = subprocess.run( + [ + sys.executable, + "-m", + "pytest", + "--collect-only", + "--no-cov", + "-q", + str(Path(__file__).parent), + ], + capture_output=True, + text=True, + timeout=60, ) - module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) - try: - module.start() - result = module.propose_grasps(cloud()) - - assert result.header.frame_id == "camera" - assert result.header.timestamp == pytest.approx(12.5) - assert result.candidates == [] - finally: - module.stop() - - -@pytest.mark.parametrize( - "points", - [ - np.array([[np.nan, 0.0, 0.0]], dtype=np.float32), - np.empty((0, 3), dtype=np.float32), - np.zeros((2, 2), dtype=np.float32), - ], -) -def test_invalid_cloud_points_are_rejected(runtime: Any, points: np.ndarray) -> None: - module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) - try: - module.start() - with pytest.raises(ValueError, match="pointcloud|XYZ"): - module.propose_grasps(cloud(points)) - runtime.return_value.infer.assert_not_called() - finally: - module.stop() - -@pytest.mark.parametrize( - "backend", - [ - (np.ones((2, 4, 4)), np.ones(1)), - (np.full((1, 4, 4), np.nan), np.ones(1)), - (np.ones((1, 4, 4)), np.array([np.inf])), - ], -) -def test_invalid_backend_outputs_are_rejected( - runtime: Any, backend: tuple[np.ndarray, np.ndarray] -) -> None: - runtime.return_value.infer.return_value = backend - module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) - try: - module.start() - with pytest.raises(ValueError): - module.propose_grasps(cloud()) - finally: - module.stop() - - -def test_inference_failure_is_wrapped(runtime: Any) -> None: - runtime.return_value.infer.side_effect = RuntimeError("backend") - module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) - try: - module.start() - with pytest.raises(GraspGenXError, match="inference"): - module.propose_grasps(cloud()) - finally: - module.stop() - - -def test_not_started_and_missing_metadata_are_rejected(runtime: Any) -> None: - module = _GraspGenXRuntimeModule(_isolated_python_runtime=True, **module_args()) - missing_frame = cloud() - missing_frame.frame_id = "" - missing_timestamp = cloud() - missing_timestamp.ts = None - try: - with pytest.raises(GraspGenXError, match="not been started"): - module.propose_grasps(cloud()) - module.start() - with pytest.raises(ValueError, match="frame_id"): - module.propose_grasps(missing_frame) - with pytest.raises(ValueError, match="timestamp"): - module.propose_grasps(missing_timestamp) - finally: - module.stop() - - -def test_runtime_implements_the_host_contract() -> None: - validate_runtime(GraspGenXModule, _GraspGenXRuntimeModule) - assert inspect.signature(GraspGenXModule.propose_grasps) == inspect.signature( - _GraspGenXRuntimeModule.propose_grasps - ) + assert result.returncode == 0, result.stdout + result.stderr + assert "test_public_adapter_import_does_not_load_optional_runtime" in result.stdout + assert "test_runtime.py" not in result.stdout diff --git a/dimos/robot/test_all_blueprints_generation.py b/dimos/robot/test_all_blueprints_generation.py index 271bebf77c..206ebfbba8 100644 --- a/dimos/robot/test_all_blueprints_generation.py +++ b/dimos/robot/test_all_blueprints_generation.py @@ -116,9 +116,7 @@ def _build_module_class_set(root: Path) -> set[str]: known: set[str] = {"Module", "ModuleBase"} all_classes: list[tuple[str, list[str]]] = [] - for path in sorted(root.rglob("*.py")): - if "__pycache__" in str(path): - continue + for path in sorted(_get_all_python_files(root)): try: tree = ast.parse(path.read_text("utf-8"), str(path)) except Exception: @@ -256,11 +254,17 @@ def _check_for_uncommitted_changes(file_path: Path) -> bool: def _get_all_python_files(root: Path) -> Generator[Path, None, None]: - for path in root.rglob("*.py"): - rel_path = str(path.relative_to(root.parent)) - if "__pycache__" in str(path) or rel_path in IGNORED_FILES: - continue - yield path + for directory, children, filenames in os.walk(root): + parent = Path(directory) + children[:] = [ + name + for name in children + if name != "__pycache__" and not (parent / name / "pyproject.toml").is_file() + ] + for name in filenames: + path = parent / name + if path.suffix == ".py" and str(path.relative_to(root.parent)) not in IGNORED_FILES: + yield path def _path_to_module_name(path: Path, root: Path) -> str: @@ -325,3 +329,26 @@ def _ends_with_blueprint_method(node: ast.expr) -> bool: if isinstance(func, ast.Attribute) and func.attr in BLUEPRINT_METHODS: return True return False + + +def test_nested_projects_do_not_contribute_modules_or_blueprints(tmp_path: Path) -> None: + root = tmp_path / "dimos" + runtime = root / "provider/python" + runtime.mkdir(parents=True) + (runtime / "pyproject.toml").write_text('[project]\nname = "runtime"\n') + (root / "provider/contract.py").write_text( + "class HostContract(Module): pass\n" + "class RuntimeOnlyBase: pass\n" + "class NotAModule(RuntimeOnlyBase): pass\n" + "host_blueprint = HostContract.blueprint()\n" + ) + (runtime / "runtime.py").write_text( + "class PublicRuntime(HostContract): pass\n" + "class RuntimeOnlyBase(Module): pass\n" + "runtime_blueprint = PublicRuntime.blueprint()\n" + ) + + blueprints, modules = _scan_for_blueprints(root) + + assert blueprints == {"host-blueprint": "dimos.provider.contract:host_blueprint"} + assert modules == {"host-contract": "dimos.provider.contract.HostContract"} diff --git a/docs/capabilities/manipulation/xarm-grasp.md b/docs/capabilities/manipulation/xarm-grasp.md index efc0dcbed8..c9ed46d91f 100644 --- a/docs/capabilities/manipulation/xarm-grasp.md +++ b/docs/capabilities/manipulation/xarm-grasp.md @@ -24,13 +24,13 @@ top; drive those with `dimos agent-send "..."`. on Linux x86_64 with a CUDA GPU compatible with Torch 2.7.1 / CUDA 12.8. Install `uv >=0.9.25` on PATH. The first launch automatically prepares the separately locked dependencies; -no GraspGenX extra is installed in the main DimOS environment. Virtual environments +no GraspGenX extra is installed in the main dimOS environment. Virtual environments live under `~/.cache/dimos/isolated-python` (or `$XDG_CACHE_HOME/dimos/isolated-python`). Pinned checkpoints download from Hugging Face during module startup and reuse its normal cache under `~/.cache/huggingface`. -Source checkouts supply their current DimOS code to the runtime. Wheel installations -resolve the exact installed DimOS version from the configured package source; that +Source checkouts supply their current dimOS code to the runtime. Wheel installations +resolve the exact installed dimOS version from the configured package source; that version must be available there. For a locally built wheel, make it available through uv's `--find-links` configuration (`UV_FIND_LINKS`) before launching. @@ -44,6 +44,20 @@ This checks the actual isolated environment, loads the model, proposes grasps fo a recorded object cloud, and verifies output and process cleanup. Missing CUDA or failed model initialization fails the check. +The proposal behavior tests run separately in the runtime project, with the model +backend mocked. From the repository root: + +```bash +cd dimos/manipulation/grasping/grasp_gen_x/python +UV_PROJECT_ENVIRONMENT="${XDG_CACHE_HOME:-$HOME/.cache}/dimos/graspgenx-tests" \ + uv run --frozen --group tests --with-editable ../../../../.. \ + python -m pytest -c pyproject.toml --confcutdir=. +``` + +The host test suite covers the public contract and excludes nested Python projects. +CI runs the runtime suite once on Linux x86_64 with Python 3.12; it needs no GPU or +checkpoint download. + What differs between the arm and the sim is decided at import time: the hardware adapter, the base pose, the camera (RealSense plus its mount edge, versus the MuJoCo wrist camera), the detector backends, and the home pose. diff --git a/pyproject.toml b/pyproject.toml index 1ee9f52708..076c1eb1ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,6 +61,7 @@ exclude = [ # and the native (C++/nix) build tree, which is built separately. "dimos" = [ "manipulation/grasping/grasp_gen_x/fixtures/**", + "manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/test_*.py", "agents/fixtures/**", "agents/mcp/fixtures/**", "mapping/google_maps/fixtures/**", From 6ddc741678ce5d5194368420274afec3d6e1f64a Mon Sep 17 00:00:00 2001 From: cc Date: Sat, 12 Sep 2026 16:52:18 -0700 Subject: [PATCH 03/12] ci: defer isolated runtime tests to shared CI support --- .github/workflows/ci.yml | 9 --------- docs/capabilities/manipulation/xarm-grasp.md | 4 ++-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d211a5b99..18312487a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -667,15 +667,6 @@ jobs: run: uv sync --group tests --frozen - name: Run tests run: uv run pytest --numprocesses=logical --cov=dimos/ --junitxml=junit.xml -m 'not (self_hosted or mujoco or self_hosted_large or web_browser or bake_e2e or native_e2e)' - - name: Run isolated GraspGenX runtime tests - if: runner.os == 'Linux' && runner.arch == 'X64' && matrix.pyver == '3.12' - working-directory: dimos/manipulation/grasping/grasp_gen_x/python - env: - UV_PROJECT_ENVIRONMENT: ${{ runner.temp }}/graspgenx-tests - run: >- - uv run --frozen --group tests --with-editable "$GITHUB_WORKSPACE" - python -m pytest -c pyproject.toml --confcutdir=. - --junitxml="$GITHUB_WORKSPACE/junit-graspgenx.xml" - name: Re-run the failing tests with maximum verbosity if: failure() env: diff --git a/docs/capabilities/manipulation/xarm-grasp.md b/docs/capabilities/manipulation/xarm-grasp.md index c9ed46d91f..b51b1b8fd3 100644 --- a/docs/capabilities/manipulation/xarm-grasp.md +++ b/docs/capabilities/manipulation/xarm-grasp.md @@ -55,8 +55,8 @@ UV_PROJECT_ENVIRONMENT="${XDG_CACHE_HOME:-$HOME/.cache}/dimos/graspgenx-tests" \ ``` The host test suite covers the public contract and excludes nested Python projects. -CI runs the runtime suite once on Linux x86_64 with Python 3.12; it needs no GPU or -checkpoint download. +Run the runtime suite manually on Linux x86_64 with Python 3.12; it needs no GPU +or checkpoint download. What differs between the arm and the sim is decided at import time: the hardware adapter, the base pose, the camera (RealSense plus its mount edge, versus the From 78b086fa9e03a65fc58ed6d90a35ad1ef0ee1786 Mon Sep 17 00:00:00 2001 From: cc Date: Sat, 12 Sep 2026 17:03:32 -0700 Subject: [PATCH 04/12] fix(isolated-python): follow host installation source --- dimos/experimental/isolated_python/README.md | 15 ++- dimos/experimental/isolated_python/module.py | 58 +++++++++- .../isolated_python/test_module.py | 109 ++++++++++++++---- docs/capabilities/manipulation/xarm-grasp.md | 21 +++- 4 files changed, 168 insertions(+), 35 deletions(-) diff --git a/dimos/experimental/isolated_python/README.md b/dimos/experimental/isolated_python/README.md index 97cb646d6c..c6ff52aed3 100644 --- a/dimos/experimental/isolated_python/README.md +++ b/dimos/experimental/isolated_python/README.md @@ -71,10 +71,17 @@ exists, Pixi supplies `uv`. If `uv.lock` exists, dimOS uses `--frozen` and treat the lockfile as the source of truth. Source checkouts make the current dimOS checkout available to the runtime. -Installed hosts resolve `dimos==`; that exact version must be -available from the configured package source. Local wheel installations can supply -the candidate wheel through `UV_FIND_LINKS`. The sibling project's `.python-version` -and `requires-python` select its Python version. Environments are stored under the DimOS cache directory in +Installed hosts follow their recorded `direct_url.json` source: Git installations +reuse the resolved commit, direct wheels and archives reuse the original URL and +recorded hash, and local directory installations reuse that directory's current +contents. These sources must remain accessible when uv prepares the environment; +unpublished Git commits need no matching PyPI release. Unsupported or invalid +metadata fails preparation, without substituting an index build. Index installations +have no direct-source metadata and intentionally use unpinned `dimos`; host/runtime +version compatibility is not guaranteed in that case. + +The sibling project's `.python-version` and `requires-python` select its Python +version. Environments are stored under the dimOS cache directory in `isolated-python//.venv`, so projects do not share environments. Preparation also warms the DimOS overlay before starting the readiness deadline. diff --git a/dimos/experimental/isolated_python/module.py b/dimos/experimental/isolated_python/module.py index a99ffa966b..502af8c523 100644 --- a/dimos/experimental/isolated_python/module.py +++ b/dimos/experimental/isolated_python/module.py @@ -17,8 +17,9 @@ from __future__ import annotations from hashlib import sha256 -from importlib.metadata import version +from importlib.metadata import distribution import inspect +import json import os from pathlib import Path import pickle @@ -27,6 +28,7 @@ import threading import time from typing import Any, ClassVar +from urllib.parse import urlencode, urlsplit, urlunsplit from dimos.constants import CACHE_DIR, DIMOS_PROJECT_ROOT from dimos.core.core import rpc @@ -39,6 +41,58 @@ logger = setup_logger() +def _installed_dimos_requirement() -> str: + """Reuse a direct installation's source; leave index installations unpinned.""" + recorded = distribution("dimos").read_text("direct_url.json") + if recorded is None: + # Installed index hosts intentionally accept the newest compatible DimOS. + return "dimos" + try: + origin = json.loads(recorded) + url = origin["url"] + if not isinstance(url, str) or not urlsplit(url).scheme: + raise ValueError("expected an absolute source URL") + kinds = [key for key in ("vcs_info", "archive_info", "dir_info") if key in origin] + if len(kinds) != 1 or not isinstance(origin[kinds[0]], dict): + raise ValueError("expected one installation source type") + kind = kinds[0] + info = origin[kind] + parts = urlsplit(url) + fragments = [parts.fragment] if parts.fragment else [] + source = urlunsplit(parts._replace(fragment="")) + if kind == "vcs_info": + if info["vcs"] != "git": + raise ValueError("only Git installation sources are supported") + commit = info["commit_id"] + if not isinstance(commit, str) or not commit: + raise ValueError("missing resolved Git commit") + source = f"git+{source}@{commit}" + elif kind == "archive_info": + hashes = info.get("hashes", {}) + if not isinstance(hashes, dict) or any( + not isinstance(key, str) or not isinstance(value, str) or not value + for key, value in hashes.items() + ): + raise ValueError("invalid archive hashes") + if hashes: + algorithm = "sha256" if "sha256" in hashes else sorted(hashes)[0] + fragments.append(urlencode({algorithm: hashes[algorithm]})) + elif parts.scheme != "file": + raise ValueError("local directory sources must use a file URL") + if "subdirectory" in origin: + subdirectory = origin["subdirectory"] + if not isinstance(subdirectory, str) or not subdirectory: + raise ValueError("invalid project subdirectory") + fragments.append(urlencode({"subdirectory": subdirectory})) + if fragments: + source += "#" + "&".join(fragments) + return f"dimos @ {source}" + except (ValueError, KeyError, TypeError) as error: + raise RuntimeError( + "Cannot select isolated dimOS source: invalid or unsupported direct_url.json" + ) from error + + def isolated_python_run_command(project: Path, *command: str) -> list[str]: """Run a command with the host DimOS available in an isolated project.""" args = ["uv", "run"] @@ -47,7 +101,7 @@ def isolated_python_run_command(project: Path, *command: str) -> list[str]: if (DIMOS_PROJECT_ROOT / "pyproject.toml").is_file(): args.extend(("--with-editable", str(DIMOS_PROJECT_ROOT))) else: - args.extend(("--with", f"dimos=={version('dimos')}")) + args.extend(("--with", _installed_dimos_requirement())) args.extend(command) if (project / "pixi.toml").is_file(): return ["pixi", "run", "--executable", *args] diff --git a/dimos/experimental/isolated_python/test_module.py b/dimos/experimental/isolated_python/test_module.py index d9b29c1ca7..6bf1da5a24 100644 --- a/dimos/experimental/isolated_python/test_module.py +++ b/dimos/experimental/isolated_python/test_module.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json from pathlib import Path import subprocess @@ -80,31 +81,89 @@ def test_uv_lock_enables_frozen_commands(tmp_path: Path, monkeypatch: pytest.Mon module.stop() -def test_installed_host_uses_matching_dimos_version( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - source = tmp_path / "contract.py" - source.touch() - project = tmp_path / "python" - project.mkdir() - (project / "pyproject.toml").touch() - installed_root = tmp_path / "site-packages" - installed_root.mkdir() - monkeypatch.setattr("dimos.experimental.isolated_python.module.version", lambda _: "9.8.7") - monkeypatch.setattr( - "dimos.experimental.isolated_python.module.inspect.getfile", lambda _: str(source) - ) - monkeypatch.setattr( - "dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", installed_root - ) - module = Contract() - try: - command = module._launch_command(7) - - assert command[:4] == ["uv", "run", "--with", "dimos==9.8.7"] - assert "--python" not in command - finally: - module.stop() +@pytest.mark.parametrize( + ("origin", "requirement"), + [ + (None, "dimos"), + ( + { + "url": "https://github.com/dimensionalOS/dimos.git", + "vcs_info": {"vcs": "git", "requested_revision": "main", "commit_id": "a" * 40}, + }, + "dimos @ git+https://github.com/dimensionalOS/dimos.git@" + "a" * 40, + ), + ( + { + "url": "ssh://git@example.com/repo.git", + "vcs_info": {"vcs": "git", "commit_id": "b" * 40}, + "subdirectory": "packages/dimos", + }, + "dimos @ git+ssh://git@example.com/repo.git@" + + "b" * 40 + + "#subdirectory=packages%2Fdimos", + ), + ( + { + "url": "https://example.com/dimos.whl", + "archive_info": {"hashes": {"sha256": "c" * 64}}, + }, + "dimos @ https://example.com/dimos.whl#sha256=" + "c" * 64, + ), + ( + {"url": "file:///tmp/dimos.whl", "archive_info": {}}, + "dimos @ file:///tmp/dimos.whl", + ), + ( + { + "url": "https://example.com/source.tar.gz", + "archive_info": {"hashes": {"sha256": "d" * 64}}, + "subdirectory": "dimos", + }, + "dimos @ https://example.com/source.tar.gz#sha256=" + "d" * 64 + "&subdirectory=dimos", + ), + ( + {"url": "file:///tmp/dimos%20source", "dir_info": {}}, + "dimos @ file:///tmp/dimos%20source", + ), + ], +) +def test_installed_host_follows_source(tmp_path, monkeypatch, mocker, origin, requirement): + monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", tmp_path) + metadata = mocker.patch("dimos.experimental.isolated_python.module.distribution") + metadata.return_value.read_text.return_value = None if origin is None else json.dumps(origin) + + command = isolated_python_run_command(tmp_path, "python", "-c", "pass") + + assert command == ["uv", "run", "--with", requirement, "python", "-c", "pass"] + metadata.assert_called_once_with("dimos") + metadata.return_value.read_text.assert_called_once_with("direct_url.json") + + +@pytest.mark.parametrize( + "recorded", + [ + "not json", + "null", + "[]", + "{}", + '{"url": "relative/path", "dir_info": {}}', + '{"url": "file:///tmp/dimos", "dir_info": {}, "archive_info": {}}', + '{"url": "https://example.com/repo", "vcs_info": {"vcs": "hg", "commit_id": "abc"}}', + '{"url": "https://example.com/repo", "vcs_info": {"vcs": "git"}}', + '{"url": "https://example.com/dimos", "dir_info": {}}', + '{"url": "file:///tmp/dimos.whl", "archive_info": {"hashes": []}}', + '{"url": "file:///tmp/dimos.whl", "archive_info": {}, "subdirectory": 42}', + ], +) +def test_invalid_installation_source_fails_without_index_fallback( + tmp_path, monkeypatch, mocker, recorded +): + monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", tmp_path) + metadata = mocker.patch("dimos.experimental.isolated_python.module.distribution") + metadata.return_value.read_text.return_value = recorded + + with pytest.raises(RuntimeError, match="invalid or unsupported direct_url.json"): + isolated_python_run_command(tmp_path, "python", "-c", "pass") def test_pixi_supplies_uv_when_manifest_exists( diff --git a/docs/capabilities/manipulation/xarm-grasp.md b/docs/capabilities/manipulation/xarm-grasp.md index b51b1b8fd3..af8d671756 100644 --- a/docs/capabilities/manipulation/xarm-grasp.md +++ b/docs/capabilities/manipulation/xarm-grasp.md @@ -29,10 +29,23 @@ live under `~/.cache/dimos/isolated-python` (or `$XDG_CACHE_HOME/dimos/isolated- Pinned checkpoints download from Hugging Face during module startup and reuse its normal cache under `~/.cache/huggingface`. -Source checkouts supply their current dimOS code to the runtime. Wheel installations -resolve the exact installed dimOS version from the configured package source; that -version must be available there. For a locally built wheel, make it available through -uv's `--find-links` configuration (`UV_FIND_LINKS`) before launching. +Source checkouts supply their current dimOS code to the runtime. Installed hosts +select the runtime's dimOS dependency using their installation source: + +| Host installation | Runtime dimOS source | +| --- | --- | +| Source checkout or editable checkout | Current checkout, installed editable | +| Package index | Unpinned `dimos` from the configured index | +| Git URL | Original repository at the host's resolved commit | +| Direct wheel or source archive | Original artifact URL, preserving its recorded hash | +| Non-editable local source directory | Recorded directory's current contents | + +Direct sources come from the installed distribution's `direct_url.json` metadata. +Git installs do not require a matching PyPI release. Recorded sources must remain +accessible when uv prepares the environment; keep locally installed wheel files +available. Invalid metadata or unavailable sources fail preparation rather than +silently selecting a different index build. Index installs intentionally allow a +different dimOS version in the child and do not guarantee host/runtime compatibility. To validate GPU proposals without moving a robot, run from a source checkout: From 316c447cb520edbc354acfe6b1f9931bc353c918 Mon Sep 17 00:00:00 2001 From: cc Date: Sat, 12 Sep 2026 17:21:32 -0700 Subject: [PATCH 05/12] fix(grasping): type-check isolated runtime in its own environment --- MANIFEST.in | 1 - dimos/experimental/isolated_python/README.md | 9 + .../grasping/grasp_gen_x/demo_inference.py | 107 ------------ .../grasping/grasp_gen_x/fixtures/LICENSE | 163 ------------------ .../grasping/grasp_gen_x/fixtures/README.md | 11 -- .../grasp_gen_x/fixtures/object_cloud.npy | Bin 31616 -> 0 bytes .../python/graspgenx_runtime/backend.py | 8 +- .../grasp_gen_x/python/pyproject.toml | 15 ++ .../grasping/grasp_gen_x/python/uv.lock | 49 ++++++ docs/capabilities/manipulation/xarm-grasp.md | 26 ++- pyproject.toml | 4 +- 11 files changed, 100 insertions(+), 293 deletions(-) delete mode 100644 dimos/manipulation/grasping/grasp_gen_x/demo_inference.py delete mode 100644 dimos/manipulation/grasping/grasp_gen_x/fixtures/LICENSE delete mode 100644 dimos/manipulation/grasping/grasp_gen_x/fixtures/README.md delete mode 100644 dimos/manipulation/grasping/grasp_gen_x/fixtures/object_cloud.npy diff --git a/MANIFEST.in b/MANIFEST.in index 9b7daddfd3..badf597a59 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -15,7 +15,6 @@ include dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml include dimos/manipulation/grasping/grasp_gen_x/python/uv.lock recursive-include dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime *.py prune dimos/manipulation/grasping/grasp_gen_x/python/.venv -recursive-exclude dimos/manipulation/grasping/grasp_gen_x/fixtures * # --- Exclusions (must come after the includes above so they win) --- # Test fixtures must never ship. diff --git a/dimos/experimental/isolated_python/README.md b/dimos/experimental/isolated_python/README.md index c6ff52aed3..f33292d3f9 100644 --- a/dimos/experimental/isolated_python/README.md +++ b/dimos/experimental/isolated_python/README.md @@ -114,3 +114,12 @@ uv run python -m dimos.experimental.isolated_python.example.run The example demonstrates streams, RPCs, skills, an injected module reference, restart behavior, and automatic shutdown. + +## Type checking + +Root `uv run mypy` excludes sibling `python/` projects, whose dependencies belong +to their own environments. Give each runtime project its own mypy configuration +and lint dependency group, then run `uv run --group lint --with-editable +python -m mypy` from that project. Use the same external `UV_PROJECT_ENVIRONMENT` +as other runtime development commands. Check the host contract from the root +project; do not suppress missing runtime dependencies in the host environment. diff --git a/dimos/manipulation/grasping/grasp_gen_x/demo_inference.py b/dimos/manipulation/grasping/grasp_gen_x/demo_inference.py deleted file mode 100644 index 6223cb936c..0000000000 --- a/dimos/manipulation/grasping/grasp_gen_x/demo_inference.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright 2026 Dimensional Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Explicit CUDA acceptance check; launches only the grasp proposal module.""" - -from __future__ import annotations - -import argparse -from pathlib import Path -import subprocess - -import numpy as np - -from dimos.experimental.isolated_python.module import isolated_python_run_command -from dimos.manipulation.grasping.grasp_gen_x.module import GraspGenXModule -from dimos.msgs.geometry_msgs.Transform import Transform -from dimos.msgs.sensor_msgs.PointCloud2 import PointCloud2 - - -def run(fixture: Path) -> None: - points = np.load(fixture, allow_pickle=False) - cloud = PointCloud2.from_numpy(points, frame_id="camera", timestamp=12.5) - # The production xArm sweep volume, in the model's +Z approach convention. - module = GraspGenXModule( - gripper={ - "extents_open": (0.0889, 0.030, 0.0370), - "offset_open": (0.0, 0.0, 0.1421), - "extents_half_open": (0.0479, 0.030, 0.0370), - "offset_half_open": (0.0, 0.0, 0.1530), - "fingertip_depth": 0.1606, - }, - grasp_frame_to_tcp=( - (0.0, 1.0, 0.0, 0.0), - (-1.0, 0.0, 0.0, 0.0), - (0.0, 0.0, 1.0, 0.172), - (0.0, 0.0, 0.0, 1.0), - ), - ) - try: - module.build() - process = module._process - assert process is not None - subprocess.run( - isolated_python_run_command( - module.runtime_project, - "python", - "-c", - "import sys, torch, torchvision, numpy; " - "assert sys.version_info[:2] == (3, 12); " - "assert torch.__version__ == '2.7.1+cu128', torch.__version__; " - "assert torchvision.__version__ == '0.22.1+cu128', torchvision.__version__; " - "assert torch.version.cuda == '12.8', torch.version.cuda; " - "assert torch.cuda.is_available(), 'CUDA unavailable'; " - "print('Runtime:', sys.executable, 'Torch:', torch.__version__, " - "'NumPy:', numpy.__version__, 'GPU:', torch.cuda.get_device_name())", - ), - cwd=module.runtime_project, - env=module._runtime_env(), - check=True, - timeout=120, - ) - module.start() - result = module.propose_grasps(cloud) - assert 0 < len(result) <= module.config.max_candidates - assert result.header.frame_id == "camera" - assert result.header.timestamp == 12.5 - scores = np.asarray([candidate.score for candidate in result]) - assert np.isfinite(scores).all() - assert np.all(scores[:-1] >= scores[1:]) - for candidate in result: - matrix = Transform.from_pose("camera", candidate.pose).to_matrix() - assert np.isfinite(matrix).all() - rotation = matrix[:3, :3] - assert np.allclose(rotation.T @ rotation, np.eye(3), atol=1e-5) - assert np.isclose(np.linalg.det(rotation), 1.0, atol=1e-5) - # Validation errors must also survive the real RPC boundary. - cloud.frame_id = "" - try: - module.propose_grasps(cloud) - except ValueError as error: - assert "frame_id" in str(error) - else: - raise AssertionError("Invalid cloud was accepted") - print(f"Validated {len(result)} ranked grasps from {len(points)} recorded points") - finally: - module.stop() - assert process.poll() is not None, "Isolated process survived module.stop()" - print("Isolated process stopped") - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument( - "--fixture", type=Path, default=Path(__file__).parent / "fixtures/object_cloud.npy" - ) - run(parser.parse_args().fixture) diff --git a/dimos/manipulation/grasping/grasp_gen_x/fixtures/LICENSE b/dimos/manipulation/grasping/grasp_gen_x/fixtures/LICENSE deleted file mode 100644 index 9675a9fbc3..0000000000 --- a/dimos/manipulation/grasping/grasp_gen_x/fixtures/LICENSE +++ /dev/null @@ -1,163 +0,0 @@ -Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - 1. Definitions. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - END OF TERMS AND CONDITIONS - - Copyright 2023-2026 NVIDIA CORPORATION & AFFILIATES - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/dimos/manipulation/grasping/grasp_gen_x/fixtures/README.md b/dimos/manipulation/grasping/grasp_gen_x/fixtures/README.md deleted file mode 100644 index 7c035409c1..0000000000 --- a/dimos/manipulation/grasping/grasp_gen_x/fixtures/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Recorded GraspGenX object cloud - -`object_cloud.npy` contains the 2,624 XYZ points from the `pc` field of -`assets/sample_data/object_pc/1741385877_42353.json` in NVlabs/GraspGenX, -revision `b9429097728cb1c430dd78b92edf17ba318aad03`. - -Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. Apache-2.0; see LICENSE. -Modified for this fixture: extracted XYZ only and converted to float32 NumPy format. -The input is a segmented object cloud in metres. Historical model outputs and -colors were omitted. Inference is stochastic; tests validate candidate invariants -rather than comparing against stored predictions. diff --git a/dimos/manipulation/grasping/grasp_gen_x/fixtures/object_cloud.npy b/dimos/manipulation/grasping/grasp_gen_x/fixtures/object_cloud.npy deleted file mode 100644 index ff15897cb3c15395eb92ae518c5760a228d89687..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31616 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 None: - model_config = load_model_cfg(_gen_dir, _dis_dir, gen_pth=None, dis_pth=None) + model_config = load_model_cfg(str(_gen_dir), str(_dis_dir), gen_pth=None, dis_pth=None) for component in ("diffusion", "discriminator"): backbone = getattr(model_config, component).gripper_backbone if backbone not in SWEEP_VOLUME_ONLY_BACKBONES: diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml b/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml index ab7443fe87..ce8caba6a4 100644 --- a/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml +++ b/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml @@ -5,12 +5,27 @@ requires-python = ">=3.12,<3.13" dependencies = ["graspgenx", "matplotlib>=3.7.1"] [dependency-groups] +lint = ["mypy==1.19.0"] tests = ["pytest==8.3.5", "pytest-mock==3.15.0", "pytest-timeout==2.4.0"] [tool.pytest.ini_options] testpaths = ["graspgenx_runtime"] addopts = "--confcutdir=. --import-mode=importlib --timeout=60 -ra" +[tool.mypy] +files = ["graspgenx_runtime"] +python_version = "3.12" +strict = true +explicit_package_bases = true +exclude = "test_.*[.]py$" +# Check this project's code, while using imported dependencies for type information. +follow_imports = "silent" + +[[tool.mypy.overrides]] +module = ["dimos", "dimos.*", "graspgenx", "graspgenx.*"] +# Both packages ship annotated source without a py.typed marker. +follow_untyped_imports = true + [tool.uv] default-groups = [] package = false diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock b/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock index 4b72189c1c..cb6d56124d 100644 --- a/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock +++ b/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock @@ -230,6 +230,9 @@ dependencies = [ ] [package.dev-dependencies] +lint = [ + { name = "mypy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] tests = [ { name = "pytest", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "pytest-mock", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, @@ -243,6 +246,7 @@ requires-dist = [ ] [package.metadata.requires-dev] +lint = [{ name = "mypy", specifier = "==1.19.0" }] tests = [ { name = "pytest", specifier = "==8.3.5" }, { name = "pytest-mock", specifier = "==3.15.0" }, @@ -617,6 +621,16 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/9c/1c928d0f5a20e4b9544d564e43ecda785f09a29ecbaa37f4e70989d0d4bd/labmaze-1.0.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d17abb69d4dfc56183afb5c317e8b2eaca0587abb3aabd2326efd3143c81f4e", size = 4875122, upload-time = "2023-10-04T17:08:11.069Z" }, ] +[[package]] +name = "librt" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/9b/356320fbae2ac8467e21c5e73e1389c80468e4998c62cc7d3536cc51b614/librt-0.15.0.tar.gz", hash = "sha256:4e66cbe84437497d951b799d3e1551291b6fb3d643820a7014b3655d57a59162", size = 214338, upload-time = "2026-08-07T10:49:42.663Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/b9/bdbb0b648b5c2befb031f4c6f3b1dd857415e8fb492a25a3c764a6681e6c/librt-0.15.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cc46cfd15022e35084355478c9ac809d90b1152222706ac9a7655ec21df6fa", size = 531904, upload-time = "2026-08-07T10:47:06.211Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4c/cf9601c1b4c5f09280acd5d83abdb2e68527a2be8257136eb42304218622/librt-0.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1e47b8ba865d7ede071a91a7163073bbaeb72541f1ef8a07d512c45c7b5007f2", size = 573688, upload-time = "2026-08-07T10:47:14.727Z" }, +] + [[package]] name = "lxml" version = "6.1.3" @@ -735,6 +749,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b1/ee/be4e1a4b7a2b27f4fb6936510d4bebcb41b0562c946930ad26916e069cf9/multidict-6.8.0-py3-none-any.whl", hash = "sha256:75daa15ca16d6285eb2e104b2f05ee6f8d9836c68da3ce5c85f615a0450eed0e", size = 16297, upload-time = "2026-09-09T13:57:56.106Z" }, ] +[[package]] +name = "mypy" +version = "1.19.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "mypy-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "pathspec", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f9/b5/b58cdc25fadd424552804bf410855d52324183112aa004f0732c5f6324cf/mypy-1.19.0.tar.gz", hash = "sha256:f6b874ca77f733222641e5c46e4711648c4037ea13646fd0cdc814c2eaec2528", size = 3579025, upload-time = "2025-11-28T15:49:01.26Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/58/cf08fff9ced0423b858f2a7495001fda28dc058136818ee9dffc31534ea9/mypy-1.19.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6016c52ab209919b46169651b362068f632efcd5eb8ef9d1735f6f86da7853b2", size = 13608336, upload-time = "2025-11-28T15:48:32.625Z" }, + { url = "https://files.pythonhosted.org/packages/64/ed/9c509105c5a6d4b73bb08733102a3ea62c25bc02c51bca85e3134bf912d3/mypy-1.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f188dcf16483b3e59f9278c4ed939ec0254aa8a60e8fc100648d9ab5ee95a431", size = 13833174, upload-time = "2025-11-28T15:45:48.091Z" }, + { url = "https://files.pythonhosted.org/packages/09/0e/fe228ed5aeab470c6f4eb82481837fadb642a5aa95cc8215fd2214822c10/mypy-1.19.0-py3-none-any.whl", hash = "sha256:0c01c99d626380752e527d5ce8e69ffbba2046eb8a060db0329690849cf9b6f9", size = 2469714, upload-time = "2025-11-28T15:45:33.22Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + [[package]] name = "narwhals" version = "2.26.0" @@ -921,6 +961,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, ] +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + [[package]] name = "pillow" version = "12.3.0" diff --git a/docs/capabilities/manipulation/xarm-grasp.md b/docs/capabilities/manipulation/xarm-grasp.md index af8d671756..aa7aca2197 100644 --- a/docs/capabilities/manipulation/xarm-grasp.md +++ b/docs/capabilities/manipulation/xarm-grasp.md @@ -47,15 +47,19 @@ available. Invalid metadata or unavailable sources fail preparation rather than silently selecting a different index build. Index installs intentionally allow a different dimOS version in the child and do not guarantee host/runtime compatibility. -To validate GPU proposals without moving a robot, run from a source checkout: +Verify GPU proposals through the existing simulated blueprint: ```bash -uv run python -m dimos.manipulation.grasping.grasp_gen_x.demo_inference +dimos --viewer none run xarm-grasp-graspgenx --simulation mujoco --daemon +dimos shell ``` -This checks the actual isolated environment, loads the model, proposes grasps for -a recorded object cloud, and verifies output and process cleanup. Missing CUDA or -failed model initialization fails the check. +Follow [Driving it](#driving-it) to scan the simulated scene, retrieve a detected +object's point cloud, and call `app.GraspGenXModule.propose_grasps(cloud)`. Confirm +that candidates are nonempty and sorted by score, poses and scores are finite, +and the result header preserves the input frame and timestamp. Starting the +blueprint alone loads the model but does not exercise inference. Stop the stack +with `dimos stop` and confirm that the isolated process exits. The proposal behavior tests run separately in the runtime project, with the model backend mocked. From the repository root: @@ -67,6 +71,18 @@ UV_PROJECT_ENVIRONMENT="${XDG_CACHE_HOME:-$HOME/.cache}/dimos/graspgenx-tests" \ python -m pytest -c pyproject.toml --confcutdir=. ``` +Type-check the runtime in its own environment: + +```bash +cd dimos/manipulation/grasping/grasp_gen_x/python +UV_PROJECT_ENVIRONMENT="${XDG_CACHE_HOME:-$HOME/.cache}/dimos/graspgenx-tests" \ + uv run --frozen --group lint --with-editable ../../../../.. python -m mypy +``` + +Root `uv run mypy` checks the host code and excludes sibling `python/` projects. +The runtime check uses the installed GraspGenX source for type information, +including its annotations despite the missing upstream `py.typed` marker. + The host test suite covers the public contract and excludes nested Python projects. Run the runtime suite manually on Linux x86_64 with Python 3.12; it needs no GPU or checkpoint download. diff --git a/pyproject.toml b/pyproject.toml index 076c1eb1ca..7be577b3ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,7 +60,6 @@ exclude = [ # Trims data the recursive globs above would otherwise pull in: test fixtures # and the native (C++/nix) build tree, which is built separately. "dimos" = [ - "manipulation/grasping/grasp_gen_x/fixtures/**", "manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/test_*.py", "agents/fixtures/**", "agents/mcp/fixtures/**", @@ -623,7 +622,8 @@ strict = true warn_unused_ignores = false untyped_calls_exclude = ["zenoh"] explicit_package_bases = true -exclude = "^dimos/models/Detic(/|$)|.*/test_.|.*/tool_.|.*/conftest.py*" +# IsolatedPythonModule owns sibling python/ projects with separate environments. +exclude = "^dimos/models/Detic(/|$)|(^|/)python/|.*/test_.|.*/tool_.|.*/conftest.py*" [[tool.mypy.overrides]] module = [ From de001be62b924c7aa60a70be97b8e90fb71118f6 Mon Sep 17 00:00:00 2001 From: cc Date: Sat, 12 Sep 2026 18:02:01 -0700 Subject: [PATCH 06/12] fix: simplify isolated sources and guard ARM test imports --- dimos/experimental/isolated_python/README.md | 34 ++++----- dimos/experimental/isolated_python/module.py | 55 +++++---------- .../isolated_python/test_module.py | 32 ++------- .../planning/spec/test_model_validation.py | 8 +++ .../utils/test_point_cloud_self_filter.py | 8 +++ .../dual_openyam/test_integration.py | 8 +++ .../xarm/blueprints/test_teleop.py | 9 +++ docs/capabilities/manipulation/xarm-grasp.md | 70 +++---------------- 8 files changed, 85 insertions(+), 139 deletions(-) diff --git a/dimos/experimental/isolated_python/README.md b/dimos/experimental/isolated_python/README.md index f33292d3f9..06bf7dbd81 100644 --- a/dimos/experimental/isolated_python/README.md +++ b/dimos/experimental/isolated_python/README.md @@ -70,15 +70,10 @@ During `build()`, dimOS runs `uv sync` in the sibling project. If `pixi.toml` exists, Pixi supplies `uv`. If `uv.lock` exists, dimOS uses `--frozen` and treats the lockfile as the source of truth. -Source checkouts make the current dimOS checkout available to the runtime. -Installed hosts follow their recorded `direct_url.json` source: Git installations -reuse the resolved commit, direct wheels and archives reuse the original URL and -recorded hash, and local directory installations reuse that directory's current -contents. These sources must remain accessible when uv prepares the environment; -unpublished Git commits need no matching PyPI release. Unsupported or invalid -metadata fails preparation, without substituting an index build. Index installations -have no direct-source metadata and intentionally use unpinned `dimos`; host/runtime -version compatibility is not guaranteed in that case. +Source checkouts use the current code. Git installations reuse the repository and +resolved commit from `direct_url.json`; direct wheels, archives, and non-editable +local directories are unsupported. Index installations use unpinned `dimos`, +with host/runtime compatibility still unresolved. The sibling project's `.python-version` and `requires-python` select its Python version. Environments are stored under the dimOS cache directory in @@ -115,11 +110,18 @@ uv run python -m dimos.experimental.isolated_python.example.run The example demonstrates streams, RPCs, skills, an injected module reference, restart behavior, and automatic shutdown. -## Type checking +## Runtime development -Root `uv run mypy` excludes sibling `python/` projects, whose dependencies belong -to their own environments. Give each runtime project its own mypy configuration -and lint dependency group, then run `uv run --group lint --with-editable -python -m mypy` from that project. Use the same external `UV_PROJECT_ENVIRONMENT` -as other runtime development commands. Check the host contract from the root -project; do not suppress missing runtime dependencies in the host environment. +Root pytest and mypy checks exclude isolated projects. Run their tests and type +checks inside their own environment. For GraspGenX, from the repository root: + +```bash +cd dimos/manipulation/grasping/grasp_gen_x/python +export UV_PROJECT_ENVIRONMENT="${XDG_CACHE_HOME:-$HOME/.cache}/dimos/graspgenx-tests" +uv run --frozen --group tests --with-editable ../../../../.. python -m pytest +uv run --frozen --group lint --with-editable ../../../../.. python -m mypy +``` + +The tests mock the model backend and need no GPU or checkpoints. Runtime mypy +reads the annotated dimOS and GraspGenX source despite their missing `py.typed` +markers. Each runtime owns its lint configuration and dependencies. diff --git a/dimos/experimental/isolated_python/module.py b/dimos/experimental/isolated_python/module.py index 502af8c523..80476cc5fe 100644 --- a/dimos/experimental/isolated_python/module.py +++ b/dimos/experimental/isolated_python/module.py @@ -28,7 +28,7 @@ import threading import time from typing import Any, ClassVar -from urllib.parse import urlencode, urlsplit, urlunsplit +from urllib.parse import urlencode, urlsplit from dimos.constants import CACHE_DIR, DIMOS_PROJECT_ROOT from dimos.core.core import rpc @@ -42,54 +42,37 @@ def _installed_dimos_requirement() -> str: - """Reuse a direct installation's source; leave index installations unpinned.""" + """Reuse a Git installation's commit; leave index installations unpinned.""" recorded = distribution("dimos").read_text("direct_url.json") if recorded is None: - # Installed index hosts intentionally accept the newest compatible DimOS. + # TODO: Define compatibility for PyPI-installed hosts. return "dimos" try: origin = json.loads(recorded) url = origin["url"] - if not isinstance(url, str) or not urlsplit(url).scheme: - raise ValueError("expected an absolute source URL") - kinds = [key for key in ("vcs_info", "archive_info", "dir_info") if key in origin] - if len(kinds) != 1 or not isinstance(origin[kinds[0]], dict): - raise ValueError("expected one installation source type") - kind = kinds[0] - info = origin[kind] - parts = urlsplit(url) - fragments = [parts.fragment] if parts.fragment else [] - source = urlunsplit(parts._replace(fragment="")) - if kind == "vcs_info": - if info["vcs"] != "git": - raise ValueError("only Git installation sources are supported") - commit = info["commit_id"] - if not isinstance(commit, str) or not commit: - raise ValueError("missing resolved Git commit") - source = f"git+{source}@{commit}" - elif kind == "archive_info": - hashes = info.get("hashes", {}) - if not isinstance(hashes, dict) or any( - not isinstance(key, str) or not isinstance(value, str) or not value - for key, value in hashes.items() - ): - raise ValueError("invalid archive hashes") - if hashes: - algorithm = "sha256" if "sha256" in hashes else sorted(hashes)[0] - fragments.append(urlencode({algorithm: hashes[algorithm]})) - elif parts.scheme != "file": - raise ValueError("local directory sources must use a file URL") + vcs = origin["vcs_info"] + commit = vcs["commit_id"] + if ( + vcs["vcs"] != "git" + or not isinstance(url, str) + or not urlsplit(url).scheme + or not isinstance(commit, str) + or not commit + or "archive_info" in origin + or "dir_info" in origin + ): + raise ValueError("expected a Git URL and resolved commit") + source = f"git+{url}@{commit}" if "subdirectory" in origin: subdirectory = origin["subdirectory"] if not isinstance(subdirectory, str) or not subdirectory: raise ValueError("invalid project subdirectory") - fragments.append(urlencode({"subdirectory": subdirectory})) - if fragments: - source += "#" + "&".join(fragments) + source += "#" + urlencode({"subdirectory": subdirectory}) return f"dimos @ {source}" except (ValueError, KeyError, TypeError) as error: raise RuntimeError( - "Cannot select isolated dimOS source: invalid or unsupported direct_url.json" + "Cannot select isolated dimOS source: invalid or unsupported direct_url.json; " + "use a source checkout or install dimOS from Git" ) from error diff --git a/dimos/experimental/isolated_python/test_module.py b/dimos/experimental/isolated_python/test_module.py index 6bf1da5a24..06684508e7 100644 --- a/dimos/experimental/isolated_python/test_module.py +++ b/dimos/experimental/isolated_python/test_module.py @@ -102,29 +102,6 @@ def test_uv_lock_enables_frozen_commands(tmp_path: Path, monkeypatch: pytest.Mon + "b" * 40 + "#subdirectory=packages%2Fdimos", ), - ( - { - "url": "https://example.com/dimos.whl", - "archive_info": {"hashes": {"sha256": "c" * 64}}, - }, - "dimos @ https://example.com/dimos.whl#sha256=" + "c" * 64, - ), - ( - {"url": "file:///tmp/dimos.whl", "archive_info": {}}, - "dimos @ file:///tmp/dimos.whl", - ), - ( - { - "url": "https://example.com/source.tar.gz", - "archive_info": {"hashes": {"sha256": "d" * 64}}, - "subdirectory": "dimos", - }, - "dimos @ https://example.com/source.tar.gz#sha256=" + "d" * 64 + "&subdirectory=dimos", - ), - ( - {"url": "file:///tmp/dimos%20source", "dir_info": {}}, - "dimos @ file:///tmp/dimos%20source", - ), ], ) def test_installed_host_follows_source(tmp_path, monkeypatch, mocker, origin, requirement): @@ -146,13 +123,16 @@ def test_installed_host_follows_source(tmp_path, monkeypatch, mocker, origin, re "null", "[]", "{}", - '{"url": "relative/path", "dir_info": {}}', + '{"url": "relative/path", "vcs_info": {"vcs": "git", "commit_id": "abc"}}', '{"url": "file:///tmp/dimos", "dir_info": {}, "archive_info": {}}', '{"url": "https://example.com/repo", "vcs_info": {"vcs": "hg", "commit_id": "abc"}}', '{"url": "https://example.com/repo", "vcs_info": {"vcs": "git"}}', '{"url": "https://example.com/dimos", "dir_info": {}}', - '{"url": "file:///tmp/dimos.whl", "archive_info": {"hashes": []}}', - '{"url": "file:///tmp/dimos.whl", "archive_info": {}, "subdirectory": 42}', + '{"url": "file:///tmp/dimos.whl", "archive_info": {}}', + '{"url": "https://example.com/dimos.tar.gz", "archive_info": {}}', + '{"url": "file:///tmp/dimos", "dir_info": {}}', + '{"url": "https://example.com/repo", "vcs_info": {"vcs": "git", "commit_id": ""}}', + '{"url": "https://example.com/repo", "vcs_info": {"vcs": "git", "commit_id": "abc"}, "subdirectory": 42}', ], ) def test_invalid_installation_source_fails_without_index_fallback( diff --git a/dimos/manipulation/planning/spec/test_model_validation.py b/dimos/manipulation/planning/spec/test_model_validation.py index 69307aea38..8605233799 100644 --- a/dimos/manipulation/planning/spec/test_model_validation.py +++ b/dimos/manipulation/planning/spec/test_model_validation.py @@ -22,8 +22,16 @@ from io import BytesIO from pathlib import Path +import platform +import sys import pytest + +# The tests extra excludes yourdfpy on Linux ARM because embreex has no wheel. +if sys.platform == "linux" and platform.machine() == "aarch64": + pytest.importorskip( + "yourdfpy", reason="yourdfpy is unavailable in the Linux ARM test environment" + ) from yourdfpy import URDF # type: ignore[import-untyped] from dimos.manipulation.planning.groups.models import PlanningGroupDefinition diff --git a/dimos/manipulation/planning/utils/test_point_cloud_self_filter.py b/dimos/manipulation/planning/utils/test_point_cloud_self_filter.py index 3b09e88a8b..b24d352e30 100644 --- a/dimos/manipulation/planning/utils/test_point_cloud_self_filter.py +++ b/dimos/manipulation/planning/utils/test_point_cloud_self_filter.py @@ -16,11 +16,19 @@ from collections.abc import Callable, Iterator from pathlib import Path +import platform +import sys from typing import Any, cast import numpy as np import pytest +# The tests extra excludes yourdfpy on Linux ARM because embreex has no wheel. +if sys.platform == "linux" and platform.machine() == "aarch64": + pytest.importorskip( + "yourdfpy", reason="yourdfpy is unavailable in the Linux ARM test environment" + ) + from dimos.manipulation.planning.utils.point_cloud_self_filter import PointCloudSelfFilter from dimos.msgs.geometry_msgs.Transform import Transform from dimos.msgs.geometry_msgs.Vector3 import Vector3 diff --git a/dimos/robot/manipulators/dual_openyam/test_integration.py b/dimos/robot/manipulators/dual_openyam/test_integration.py index ca90114388..442950bfb3 100644 --- a/dimos/robot/manipulators/dual_openyam/test_integration.py +++ b/dimos/robot/manipulators/dual_openyam/test_integration.py @@ -13,8 +13,16 @@ # limitations under the License. from io import BytesIO +import platform +import sys import pytest + +# The tests extra excludes yourdfpy on Linux ARM because embreex has no wheel. +if sys.platform == "linux" and platform.machine() == "aarch64": + pytest.importorskip( + "yourdfpy", reason="yourdfpy is unavailable in the Linux ARM test environment" + ) from yourdfpy import URDF # type: ignore[import-untyped] from dimos.manipulation.planning.utils.mesh_utils import prepare_urdf_for_drake diff --git a/dimos/robot/manipulators/xarm/blueprints/test_teleop.py b/dimos/robot/manipulators/xarm/blueprints/test_teleop.py index 8cd1a0afbb..fef0f0ba57 100644 --- a/dimos/robot/manipulators/xarm/blueprints/test_teleop.py +++ b/dimos/robot/manipulators/xarm/blueprints/test_teleop.py @@ -12,8 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +import platform +import sys + import pytest +# The tests extra excludes yourdfpy on Linux ARM because embreex has no wheel. +if sys.platform == "linux" and platform.machine() == "aarch64": + pytest.importorskip( + "yourdfpy", reason="yourdfpy is unavailable in the Linux ARM test environment" + ) + from dimos.control.coordinator import ControlCoordinator, TaskConfig from dimos.core.coordination.blueprints import Blueprint from dimos.manipulation.manipulation_module import ( diff --git a/docs/capabilities/manipulation/xarm-grasp.md b/docs/capabilities/manipulation/xarm-grasp.md index aa7aca2197..c25ff75bd7 100644 --- a/docs/capabilities/manipulation/xarm-grasp.md +++ b/docs/capabilities/manipulation/xarm-grasp.md @@ -20,72 +20,20 @@ Miss `--xarm7-ip` on hardware and the arm has no address to reach; leave `xarm-grasp-agent` and `xarm-grasp-graspgenx-agent` add an MCP agent over the top; drive those with `dimos agent-send "..."`. -`xarm-grasp-graspgenx` runs GraspGenX in an isolated Python 3.12 environment -on Linux x86_64 with a CUDA GPU compatible with Torch 2.7.1 / CUDA 12.8. -Install `uv >=0.9.25` on PATH. The first launch automatically prepares the separately -locked dependencies; -no GraspGenX extra is installed in the main dimOS environment. Virtual environments -live under `~/.cache/dimos/isolated-python` (or `$XDG_CACHE_HOME/dimos/isolated-python`). -Pinned checkpoints download from Hugging Face during module startup and reuse -its normal cache under `~/.cache/huggingface`. - -Source checkouts supply their current dimOS code to the runtime. Installed hosts -select the runtime's dimOS dependency using their installation source: - -| Host installation | Runtime dimOS source | -| --- | --- | -| Source checkout or editable checkout | Current checkout, installed editable | -| Package index | Unpinned `dimos` from the configured index | -| Git URL | Original repository at the host's resolved commit | -| Direct wheel or source archive | Original artifact URL, preserving its recorded hash | -| Non-editable local source directory | Recorded directory's current contents | - -Direct sources come from the installed distribution's `direct_url.json` metadata. -Git installs do not require a matching PyPI release. Recorded sources must remain -accessible when uv prepares the environment; keep locally installed wheel files -available. Invalid metadata or unavailable sources fail preparation rather than -silently selecting a different index build. Index installs intentionally allow a -different dimOS version in the child and do not guarantee host/runtime compatibility. - -Verify GPU proposals through the existing simulated blueprint: +GraspGenX requires Linux x86_64, a CUDA 12.8-compatible GPU, and `uv >=0.9.25`. +The first launch prepares its isolated Python 3.12 environment and downloads the +checkpoints. Use a source checkout or Git install; PyPI compatibility is unresolved. -```bash -dimos --viewer none run xarm-grasp-graspgenx --simulation mujoco --daemon -dimos shell -``` - -Follow [Driving it](#driving-it) to scan the simulated scene, retrieve a detected -object's point cloud, and call `app.GraspGenXModule.propose_grasps(cloud)`. Confirm -that candidates are nonempty and sorted by score, poses and scores are finite, -and the result header preserves the input frame and timestamp. Starting the -blueprint alone loads the model but does not exercise inference. Stop the stack -with `dimos stop` and confirm that the isolated process exits. - -The proposal behavior tests run separately in the runtime project, with the model -backend mocked. From the repository root: +To show the MuJoCo window with Rerun disabled: ```bash -cd dimos/manipulation/grasping/grasp_gen_x/python -UV_PROJECT_ENVIRONMENT="${XDG_CACHE_HOME:-$HOME/.cache}/dimos/graspgenx-tests" \ - uv run --frozen --group tests --with-editable ../../../../.. \ - python -m pytest -c pyproject.toml --confcutdir=. +MUJOCO_GL=glfw dimos --viewer none run xarm-grasp-graspgenx \ + --simulation mujoco --headless false ``` -Type-check the runtime in its own environment: - -```bash -cd dimos/manipulation/grasping/grasp_gen_x/python -UV_PROJECT_ENVIRONMENT="${XDG_CACHE_HOME:-$HOME/.cache}/dimos/graspgenx-tests" \ - uv run --frozen --group lint --with-editable ../../../../.. python -m mypy -``` - -Root `uv run mypy` checks the host code and excludes sibling `python/` projects. -The runtime check uses the installed GraspGenX source for type information, -including its annotations despite the missing upstream `py.typed` marker. - -The host test suite covers the public contract and excludes nested Python projects. -Run the runtime suite manually on Linux x86_64 with Python 3.12; it needs no GPU -or checkpoint download. +In another terminal, use `dimos shell` and follow [Driving it](#driving-it) to scan +objects and request grasps. See the [isolated-runtime development guide](/dimos/experimental/isolated_python/README.md#runtime-development) +for test and type-check commands. What differs between the arm and the sim is decided at import time: the hardware adapter, the base pose, the camera (RealSense plus its mount edge, versus the From a30843f294f24dbd1d3c08467e8665147383a48d Mon Sep 17 00:00:00 2001 From: cc Date: Sat, 12 Sep 2026 18:11:42 -0700 Subject: [PATCH 07/12] fix: isolate nested pytest collection from xdist watchdog --- dimos/manipulation/grasping/grasp_gen_x/test_module.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dimos/manipulation/grasping/grasp_gen_x/test_module.py b/dimos/manipulation/grasping/grasp_gen_x/test_module.py index ceed6d34d2..55d7e4c301 100644 --- a/dimos/manipulation/grasping/grasp_gen_x/test_module.py +++ b/dimos/manipulation/grasping/grasp_gen_x/test_module.py @@ -17,6 +17,7 @@ from __future__ import annotations import inspect +import os from pathlib import Path import subprocess import sys @@ -133,6 +134,9 @@ def test_rigid_transform_relational_validation() -> None: def test_host_collection_excludes_the_nested_runtime_suite() -> None: + # This is a new pytest controller, not an outer xdist worker. Give it its + # own run ID so its watchdog cannot sweep the parent session's workers. + env = {key: value for key, value in os.environ.items() if not key.startswith("PYTEST_XDIST_")} result = subprocess.run( [ sys.executable, @@ -146,6 +150,7 @@ def test_host_collection_excludes_the_nested_runtime_suite() -> None: capture_output=True, text=True, timeout=60, + env=env, ) assert result.returncode == 0, result.stdout + result.stderr From 3fc930adb7746c9eafaf12cffd30c8e1942cc95a Mon Sep 17 00:00:00 2001 From: cc <55869557+TomCC7@users.noreply.github.com> Date: Sat, 12 Sep 2026 19:14:28 -0700 Subject: [PATCH 08/12] Update dimos/experimental/isolated_python/module.py --- dimos/experimental/isolated_python/module.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dimos/experimental/isolated_python/module.py b/dimos/experimental/isolated_python/module.py index 80476cc5fe..79ed028db5 100644 --- a/dimos/experimental/isolated_python/module.py +++ b/dimos/experimental/isolated_python/module.py @@ -41,6 +41,7 @@ logger = setup_logger() +# TODO: sorry but I don't find a good way to ensure pypi installation compatibility, will address later def _installed_dimos_requirement() -> str: """Reuse a Git installation's commit; leave index installations unpinned.""" recorded = distribution("dimos").read_text("direct_url.json") From 3e54402d67b6007e5bc723f83fc1935ed5fc1fad Mon Sep 17 00:00:00 2001 From: cc Date: Sat, 12 Sep 2026 19:16:08 -0700 Subject: [PATCH 09/12] fix: wait for module shutdown before redeploying --- dimos/core/coordination/module_coordinator.py | 15 +++++-------- .../coordination/test_module_coordinator.py | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/dimos/core/coordination/module_coordinator.py b/dimos/core/coordination/module_coordinator.py index a4d6941b09..7c0e8b11cd 100644 --- a/dimos/core/coordination/module_coordinator.py +++ b/dimos/core/coordination/module_coordinator.py @@ -51,7 +51,7 @@ if TYPE_CHECKING: from dimos.core.coordination.blueprint_config.parsed import ParsedBlueprintConfig from dimos.core.coordination.blueprints import Blueprint, BlueprintAtom - from dimos.core.rpc_client import ModuleProxy, ModuleProxyProtocol + from dimos.core.rpc_client import ModuleProxy, ModuleProxyProtocol, RPCClient logger = setup_logger() @@ -486,17 +486,10 @@ def _unload_module(self, module: type[ModuleBase] | str) -> None: proxy = self._deployed_modules[name] - try: - proxy.stop() - except Exception: - logger.error( - "Error stopping module during unload", - module=name, - exc_info=True, - ) - python_wm = cast("WorkerManagerPython", self._managers["python"]) try: + # The worker pipe waits for stop() to finish. An RPC stop is + # fire-and-forget and would race this call and the replacement. python_wm.undeploy(proxy) except Exception: logger.error( @@ -504,6 +497,8 @@ def _unload_module(self, module: type[ModuleBase] | str) -> None: module=name, exc_info=True, ) + finally: + cast("RPCClient", proxy).stop_rpc_client() del self._deployed_modules[name] del self._instance_classes[name] diff --git a/dimos/core/coordination/test_module_coordinator.py b/dimos/core/coordination/test_module_coordinator.py index 8cc222147b..5a7b87808f 100644 --- a/dimos/core/coordination/test_module_coordinator.py +++ b/dimos/core/coordination/test_module_coordinator.py @@ -13,6 +13,7 @@ # limitations under the License. from collections.abc import Callable +from pathlib import Path import pickle import threading from types import MappingProxyType @@ -787,6 +788,27 @@ def test_check_requirements_failure(mocker) -> None: _check_requirements(bp) +class ShutdownRecorder(Module): + def __init__(self, shutdown_log: str, **kwargs: Any) -> None: + super().__init__(**kwargs) + self._shutdown_log = Path(shutdown_log) + + @rpc + def stop(self) -> None: + with self._shutdown_log.open("a") as log: + log.write("stopped\n") + super().stop() + + +def test_unload_stops_module_once(dynamic_coordinator, tmp_path) -> None: + shutdown_log = tmp_path / "shutdown.log" + dynamic_coordinator.load_blueprint(ShutdownRecorder.blueprint(shutdown_log=str(shutdown_log))) + + dynamic_coordinator.unload_module(ShutdownRecorder) + + assert shutdown_log.read_text() == "stopped\n" + + def test_restart_module_basic(dynamic_coordinator) -> None: """restart_module replaces the deployed proxy with a fresh one.""" dynamic_coordinator.load_module(ModuleA) From 4d82f1e87e6cd58f00adf3aec0173e432e80f66f Mon Sep 17 00:00:00 2001 From: cc Date: Sat, 12 Sep 2026 19:56:37 -0700 Subject: [PATCH 10/12] Revert "fix: wait for module shutdown before redeploying" This reverts commit 3e54402d67b6007e5bc723f83fc1935ed5fc1fad. --- dimos/core/coordination/module_coordinator.py | 15 ++++++++----- .../coordination/test_module_coordinator.py | 22 ------------------- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/dimos/core/coordination/module_coordinator.py b/dimos/core/coordination/module_coordinator.py index 7c0e8b11cd..a4d6941b09 100644 --- a/dimos/core/coordination/module_coordinator.py +++ b/dimos/core/coordination/module_coordinator.py @@ -51,7 +51,7 @@ if TYPE_CHECKING: from dimos.core.coordination.blueprint_config.parsed import ParsedBlueprintConfig from dimos.core.coordination.blueprints import Blueprint, BlueprintAtom - from dimos.core.rpc_client import ModuleProxy, ModuleProxyProtocol, RPCClient + from dimos.core.rpc_client import ModuleProxy, ModuleProxyProtocol logger = setup_logger() @@ -486,10 +486,17 @@ def _unload_module(self, module: type[ModuleBase] | str) -> None: proxy = self._deployed_modules[name] + try: + proxy.stop() + except Exception: + logger.error( + "Error stopping module during unload", + module=name, + exc_info=True, + ) + python_wm = cast("WorkerManagerPython", self._managers["python"]) try: - # The worker pipe waits for stop() to finish. An RPC stop is - # fire-and-forget and would race this call and the replacement. python_wm.undeploy(proxy) except Exception: logger.error( @@ -497,8 +504,6 @@ def _unload_module(self, module: type[ModuleBase] | str) -> None: module=name, exc_info=True, ) - finally: - cast("RPCClient", proxy).stop_rpc_client() del self._deployed_modules[name] del self._instance_classes[name] diff --git a/dimos/core/coordination/test_module_coordinator.py b/dimos/core/coordination/test_module_coordinator.py index 5a7b87808f..8cc222147b 100644 --- a/dimos/core/coordination/test_module_coordinator.py +++ b/dimos/core/coordination/test_module_coordinator.py @@ -13,7 +13,6 @@ # limitations under the License. from collections.abc import Callable -from pathlib import Path import pickle import threading from types import MappingProxyType @@ -788,27 +787,6 @@ def test_check_requirements_failure(mocker) -> None: _check_requirements(bp) -class ShutdownRecorder(Module): - def __init__(self, shutdown_log: str, **kwargs: Any) -> None: - super().__init__(**kwargs) - self._shutdown_log = Path(shutdown_log) - - @rpc - def stop(self) -> None: - with self._shutdown_log.open("a") as log: - log.write("stopped\n") - super().stop() - - -def test_unload_stops_module_once(dynamic_coordinator, tmp_path) -> None: - shutdown_log = tmp_path / "shutdown.log" - dynamic_coordinator.load_blueprint(ShutdownRecorder.blueprint(shutdown_log=str(shutdown_log))) - - dynamic_coordinator.unload_module(ShutdownRecorder) - - assert shutdown_log.read_text() == "stopped\n" - - def test_restart_module_basic(dynamic_coordinator) -> None: """restart_module replaces the deployed proxy with a fresh one.""" dynamic_coordinator.load_module(ModuleA) From 3c4c4157811fdfd22ed080ec994be22459948b33 Mon Sep 17 00:00:00 2001 From: cc Date: Sun, 13 Sep 2026 01:48:39 -0700 Subject: [PATCH 11/12] fix: match isolated DimOS to host installation --- dimos/experimental/isolated_python/README.md | 10 ++- dimos/experimental/isolated_python/module.py | 66 ++++++++++++---- .../isolated_python/test_module.py | 79 ++++++++++++++++++- pyproject.toml | 1 + uv.lock | 4 +- 5 files changed, 137 insertions(+), 23 deletions(-) diff --git a/dimos/experimental/isolated_python/README.md b/dimos/experimental/isolated_python/README.md index 06bf7dbd81..95adf48330 100644 --- a/dimos/experimental/isolated_python/README.md +++ b/dimos/experimental/isolated_python/README.md @@ -70,10 +70,12 @@ During `build()`, dimOS runs `uv sync` in the sibling project. If `pixi.toml` exists, Pixi supplies `uv`. If `uv.lock` exists, dimOS uses `--frozen` and treats the lockfile as the source of truth. -Source checkouts use the current code. Git installations reuse the repository and -resolved commit from `direct_url.json`; direct wheels, archives, and non-editable -local directories are unsupported. Index installations use unpinned `dimos`, -with host/runtime compatibility still unresolved. +Source checkouts use an editable install of the same tree, including local edits. +Index installations pin `dimos` to the host's installed version. Git installations +reuse the repository and resolved commit from `direct_url.json`. Preparation and +launch use the same selection; an unavailable release fails rather than selecting +another version. Direct wheels, archives, and non-editable local directories remain +unsupported. Restart running modules after editing a checkout. The sibling project's `.python-version` and `requires-python` select its Python version. Environments are stored under the dimOS cache directory in diff --git a/dimos/experimental/isolated_python/module.py b/dimos/experimental/isolated_python/module.py index 79ed028db5..de9d5d9ef5 100644 --- a/dimos/experimental/isolated_python/module.py +++ b/dimos/experimental/isolated_python/module.py @@ -17,7 +17,7 @@ from __future__ import annotations from hashlib import sha256 -from importlib.metadata import distribution +from importlib.metadata import PackageNotFoundError, distribution import inspect import json import os @@ -25,11 +25,19 @@ import pickle import select import subprocess +import sys import threading import time from typing import Any, ClassVar from urllib.parse import urlencode, urlsplit +from packaging.utils import canonicalize_name + +if sys.version_info >= (3, 11): + import tomllib +else: + import tomli as tomllib + from dimos.constants import CACHE_DIR, DIMOS_PROJECT_ROOT from dimos.core.core import rpc from dimos.core.module import Module @@ -41,13 +49,18 @@ logger = setup_logger() -# TODO: sorry but I don't find a good way to ensure pypi installation compatibility, will address later def _installed_dimos_requirement() -> str: - """Reuse a Git installation's commit; leave index installations unpinned.""" - recorded = distribution("dimos").read_text("direct_url.json") + """Reuse the host's exact installed release or Git commit.""" + try: + installed = distribution("dimos") + except PackageNotFoundError as error: + raise RuntimeError( + "Cannot select isolated dimOS source: no dimos installation metadata; " + "use a source checkout or install dimOS from PyPI or Git" + ) from error + recorded = installed.read_text("direct_url.json") if recorded is None: - # TODO: Define compatibility for PyPI-installed hosts. - return "dimos" + return f"dimos=={installed.version}" try: origin = json.loads(recorded) url = origin["url"] @@ -73,25 +86,39 @@ def _installed_dimos_requirement() -> str: except (ValueError, KeyError, TypeError) as error: raise RuntimeError( "Cannot select isolated dimOS source: invalid or unsupported direct_url.json; " - "use a source checkout or install dimOS from Git" + "use a source checkout or install dimOS from PyPI or Git" ) from error -def isolated_python_run_command(project: Path, *command: str) -> list[str]: - """Run a command with the host DimOS available in an isolated project.""" +def _resolve_dimos_source() -> tuple[str, str]: + """Select the imported checkout, or the installed distribution's provenance.""" + root = DIMOS_PROJECT_ROOT.resolve() + manifest = root / "pyproject.toml" + if manifest.is_file(): + with manifest.open("rb") as stream: + metadata = tomllib.load(stream) + name = metadata.get("project", {}).get("name") + if isinstance(name, str) and canonicalize_name(name) == "dimos": + return "--with-editable", str(root) + return "--with", _installed_dimos_requirement() + + +def _python_run_command(project: Path, dimos_source: tuple[str, str], *command: str) -> list[str]: args = ["uv", "run"] if (project / "uv.lock").is_file(): args.append("--frozen") - if (DIMOS_PROJECT_ROOT / "pyproject.toml").is_file(): - args.extend(("--with-editable", str(DIMOS_PROJECT_ROOT))) - else: - args.extend(("--with", _installed_dimos_requirement())) + args.extend(dimos_source) args.extend(command) if (project / "pixi.toml").is_file(): return ["pixi", "run", "--executable", *args] return args +def isolated_python_run_command(project: Path, *command: str) -> list[str]: + """Run a command with the host DimOS available in an isolated project.""" + return _python_run_command(project, _resolve_dimos_source(), *command) + + class IsolatedPythonModuleConfig(NativeModuleConfig): """Process settings for an isolated Python module.""" @@ -124,12 +151,14 @@ class IsolatedPythonModule(NativeModule): _runtime_client: RPCClient | None _runtime_name: str | None _module_refs: dict[str, RPCClient] + _dimos_source: tuple[str, str] | None def __init__(self, _isolated_python_runtime: bool = False, **kwargs: Any) -> None: self._isolated_python_runtime = _isolated_python_runtime self._runtime_client = None self._runtime_name = None self._module_refs = {} + self._dimos_source = None super().__init__(**kwargs) def __getattribute__(self, name: str) -> Any: @@ -174,8 +203,7 @@ def _prepare_command(self) -> list[str]: return self._uv_command(*args) def _launch_command(self, handshake_fd: int) -> list[str]: - return isolated_python_run_command( - self.runtime_project, + return self._run_command( "python", "-m", "dimos.experimental.isolated_python.bootstrap", @@ -189,6 +217,11 @@ def _launch_command(self, handshake_fd: int) -> list[str]: str(handshake_fd), ) + def _run_command(self, *command: str) -> list[str]: + if self._dimos_source is None: + self._dimos_source = _resolve_dimos_source() + return _python_run_command(self.runtime_project, self._dimos_source, *command) + def _new_runtime_name(self) -> str: public_name = self.config.instance_name or type(self).__name__ self._runtime_name = f"__isolated_python__/{public_name}/{short_id()}" @@ -207,10 +240,11 @@ def _runtime_env(self) -> dict[str, str]: return env def _run_prepare(self) -> None: + self._dimos_source = _resolve_dimos_source() # Resolve the DimOS overlay too, before the runtime's readiness deadline. commands = [ self._prepare_command(), - isolated_python_run_command(self.runtime_project, "python", "-c", "pass"), + self._run_command("python", "-c", "pass"), ] for command in commands: result = subprocess.run( diff --git a/dimos/experimental/isolated_python/test_module.py b/dimos/experimental/isolated_python/test_module.py index 06684508e7..2923f5e298 100644 --- a/dimos/experimental/isolated_python/test_module.py +++ b/dimos/experimental/isolated_python/test_module.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from importlib.metadata import PackageNotFoundError import json from pathlib import Path import subprocess @@ -59,7 +60,7 @@ def test_uv_lock_enables_frozen_commands(tmp_path: Path, monkeypatch: pytest.Mon (project / "uv.lock").touch() checkout = tmp_path / "checkout" checkout.mkdir() - (checkout / "pyproject.toml").touch() + (checkout / "pyproject.toml").write_text('[project]\nname = "dimos"\n') monkeypatch.setattr( "dimos.experimental.isolated_python.module.inspect.getfile", lambda _: str(source) ) @@ -84,7 +85,7 @@ def test_uv_lock_enables_frozen_commands(tmp_path: Path, monkeypatch: pytest.Mon @pytest.mark.parametrize( ("origin", "requirement"), [ - (None, "dimos"), + (None, "dimos==0.0.14"), ( { "url": "https://github.com/dimensionalOS/dimos.git", @@ -107,6 +108,7 @@ def test_uv_lock_enables_frozen_commands(tmp_path: Path, monkeypatch: pytest.Mon def test_installed_host_follows_source(tmp_path, monkeypatch, mocker, origin, requirement): monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", tmp_path) metadata = mocker.patch("dimos.experimental.isolated_python.module.distribution") + metadata.return_value.version = "0.0.14" metadata.return_value.read_text.return_value = None if origin is None else json.dumps(origin) command = isolated_python_run_command(tmp_path, "python", "-c", "pass") @@ -116,6 +118,53 @@ def test_installed_host_follows_source(tmp_path, monkeypatch, mocker, origin, re metadata.return_value.read_text.assert_called_once_with("direct_url.json") +@pytest.mark.parametrize("version", ["0.0.14", "0.0.15rc1", "0.0.15.dev2", "0.0.14+custom.1"]) +def test_index_install_preserves_full_host_version(tmp_path, monkeypatch, mocker, version): + monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", tmp_path) + metadata = mocker.patch("dimos.experimental.isolated_python.module.distribution") + metadata.return_value.read_text.return_value = None + metadata.return_value.version = version + + assert isolated_python_run_command(tmp_path, "python") == [ + "uv", + "run", + "--with", + f"dimos=={version}", + "python", + ] + + +@pytest.mark.parametrize("name", ["dimos", "another-project"]) +def test_only_dimos_manifest_selects_editable_source(tmp_path, monkeypatch, mocker, name): + (tmp_path / "pyproject.toml").write_text(f'[project]\nname = "{name}"\n') + monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", tmp_path) + metadata = mocker.patch("dimos.experimental.isolated_python.module.distribution") + metadata.return_value.read_text.return_value = None + metadata.return_value.version = "0.0.14" + expected = { + "dimos": ["--with-editable", str(tmp_path)], + "another-project": ["--with", "dimos==0.0.14"], + } + + assert isolated_python_run_command(tmp_path, "python") == [ + "uv", + "run", + *expected[name], + "python", + ] + + +def test_missing_installation_metadata_has_actionable_error(tmp_path, monkeypatch, mocker): + monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", tmp_path) + mocker.patch( + "dimos.experimental.isolated_python.module.distribution", + side_effect=PackageNotFoundError("dimos"), + ) + + with pytest.raises(RuntimeError, match="no dimos installation metadata"): + isolated_python_run_command(tmp_path, "python") + + @pytest.mark.parametrize( "recorded", [ @@ -268,6 +317,32 @@ def test_preparation_warms_the_launch_environment(project: Path, mocker: MockerF module.stop() +def test_launch_reuses_prepared_host_version_until_next_build(project, monkeypatch, mocker): + monkeypatch.setattr( + "dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", project.parent + ) + metadata = mocker.patch("dimos.experimental.isolated_python.module.distribution") + metadata.return_value.read_text.return_value = None + metadata.return_value.version = "0.0.14" + run = mocker.patch( + "dimos.experimental.isolated_python.module.subprocess.run", + return_value=subprocess.CompletedProcess([], 0, "", ""), + ) + module = Contract() + try: + module._run_prepare() + metadata.return_value.version = "0.0.15" + + expected = ["uv", "run", "--frozen", "--with", "dimos==0.0.14"] + assert run.call_args.args[0][:5] == expected + assert module._launch_command(7)[:5] == expected + + module._run_prepare() + assert module._launch_command(7)[:5] == ["uv", "run", "--frozen", "--with", "dimos==0.0.15"] + finally: + module.stop() + + @pytest.mark.parametrize("failure_stage", [0, 1]) def test_preparation_failure_prevents_launch( project: Path, mocker: MockerFixture, failure_stage: int diff --git a/pyproject.toml b/pyproject.toml index 7be577b3ad..bbe217cb23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -123,6 +123,7 @@ dependencies = [ "pydantic", "python-dotenv", "packaging>=24.0", + "tomli>=2.0; python_version < '3.11'", "filelock>=3.20,<4", "annotation-protocol>=1.4.0", "lazy_loader", diff --git a/uv.lock b/uv.lock index 891b7b40cb..f79aaabd9b 100644 --- a/uv.lock +++ b/uv.lock @@ -32,7 +32,7 @@ resolution-markers = [ ] [options] -exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. +exclude-newer = "2026-09-06T08:32:08.145661055Z" exclude-newer-span = "P7D" [options.exclude-newer-package] @@ -1759,6 +1759,7 @@ dependencies = [ { name = "terminaltexteffects" }, { name = "textual" }, { name = "textual-serve" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "toolz" }, { name = "typer" }, { name = "typing-extensions" }, @@ -2453,6 +2454,7 @@ requires-dist = [ { name = "textual", specifier = "==3.7.1" }, { name = "textual-serve", specifier = ">=1.1.1,<2" }, { name = "timm", marker = "extra == 'perception'", specifier = ">=1.0.15" }, + { name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0" }, { name = "toolz", specifier = ">=1.1.0" }, { name = "torchreid", marker = "extra == 'misc'", specifier = "==0.2.5" }, { name = "transformers", extras = ["torch"], marker = "extra == 'perception'", specifier = ">=4.53.0,<4.54" }, From 5e7c204e59bafef744ab9efd6408ef14476c4ab0 Mon Sep 17 00:00:00 2001 From: cc Date: Tue, 15 Sep 2026 11:00:04 -0700 Subject: [PATCH 12/12] refactor: load isolated runtimes from shared checkout --- MANIFEST.in | 6 - dimos/experimental/isolated_python/README.md | 61 ++-- .../isolated_python/example/contract.py | 1 + dimos/experimental/isolated_python/module.py | 104 +------ .../isolated_python/test_module.py | 278 +++++------------- .../grasping/grasp_gen_x/module.py | 1 + docs/capabilities/manipulation/xarm-grasp.md | 3 +- .../python/example}/.gitignore | 0 .../python/example}/.python-version | 0 .../example}/example_external/runtime.py | 2 +- .../python/example}/pyproject.toml | 0 .../python => native/python/example}/uv.lock | 0 .../graspgenx}/graspgenx_runtime/backend.py | 0 .../graspgenx}/graspgenx_runtime/runtime.py | 0 .../graspgenx_runtime/test_runtime.py | 0 .../python/graspgenx}/pyproject.toml | 0 .../python/graspgenx}/uv.lock | 0 pyproject.toml | 14 +- uv.lock | 4 +- 19 files changed, 124 insertions(+), 350 deletions(-) rename {dimos/experimental/isolated_python/example/python => native/python/example}/.gitignore (100%) rename {dimos/experimental/isolated_python/example/python => native/python/example}/.python-version (100%) rename {dimos/experimental/isolated_python/example/python => native/python/example}/example_external/runtime.py (95%) rename {dimos/experimental/isolated_python/example/python => native/python/example}/pyproject.toml (100%) rename {dimos/experimental/isolated_python/example/python => native/python/example}/uv.lock (100%) rename {dimos/manipulation/grasping/grasp_gen_x/python => native/python/graspgenx}/graspgenx_runtime/backend.py (100%) rename {dimos/manipulation/grasping/grasp_gen_x/python => native/python/graspgenx}/graspgenx_runtime/runtime.py (100%) rename {dimos/manipulation/grasping/grasp_gen_x/python => native/python/graspgenx}/graspgenx_runtime/test_runtime.py (100%) rename {dimos/manipulation/grasping/grasp_gen_x/python => native/python/graspgenx}/pyproject.toml (100%) rename {dimos/manipulation/grasping/grasp_gen_x/python => native/python/graspgenx}/uv.lock (100%) diff --git a/MANIFEST.in b/MANIFEST.in index badf597a59..3aee0bf04e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -10,12 +10,6 @@ global-exclude .DS_Store # loading a new data type — never go back to per-package globs. recursive-include dimos *.yaml *.yml *.json *.urdf *.html *.css *.js *.svg *.tcss -# GraspGenX runs this project separately; include its manifest and locked sources. -include dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml -include dimos/manipulation/grasping/grasp_gen_x/python/uv.lock -recursive-include dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime *.py -prune dimos/manipulation/grasping/grasp_gen_x/python/.venv - # --- Exclusions (must come after the includes above so they win) --- # Test fixtures must never ship. recursive-exclude dimos/agents/fixtures * diff --git a/dimos/experimental/isolated_python/README.md b/dimos/experimental/isolated_python/README.md index 95adf48330..da5490e832 100644 --- a/dimos/experimental/isolated_python/README.md +++ b/dimos/experimental/isolated_python/README.md @@ -6,16 +6,14 @@ management. Its API is experimental and may change without compatibility aliases ## Project layout -Place the host contract beside a `python/` project that contains the concrete -runtime: +Keep the host contract in `dimos/` and its isolated project outside the package: ```text -my_module/ -├── contract.py -└── python/ - ├── pyproject.toml - └── my_runtime/ - └── runtime.py +dimos/my_module/contract.py +native/python/my_module/ +├── pyproject.toml +└── my_runtime/ + └── runtime.py ``` Define the host-visible contract: @@ -33,6 +31,7 @@ class MultiplierConfig(IsolatedPythonModuleConfig): class Multiplier(IsolatedPythonModule): + project_dir = "native/python/my_module" implementation = "my_runtime.runtime:MultiplierRuntime" config: MultiplierConfig @@ -41,13 +40,13 @@ class Multiplier(IsolatedPythonModule): raise NotImplementedError ``` -The sibling runtime imports and implements that contract: +The isolated runtime imports and implements that contract: ```python skip from typing import Any from dimos.core.core import rpc -from my_module.contract import Multiplier +from dimos.my_module.contract import Multiplier class MultiplierRuntime(Multiplier): @@ -66,27 +65,26 @@ a contract stub or changes its signature or classification. ## Runtime behavior -During `build()`, dimOS runs `uv sync` in the sibling project. If `pixi.toml` +During `build()`, dimOS runs `uv sync` in the declared project. If `pixi.toml` exists, Pixi supplies `uv`. If `uv.lock` exists, dimOS uses `--frozen` and treats the lockfile as the source of truth. -Source checkouts use an editable install of the same tree, including local edits. -Index installations pin `dimos` to the host's installed version. Git installations -reuse the repository and resolved commit from `direct_url.json`. Preparation and -launch use the same selection; an unavailable release fails rather than selecting -another version. Direct wheels, archives, and non-editable local directories remain -unsupported. Restart running modules after editing a checkout. +The runtime project and child dimOS come from `get_project_root()`, the shared +LFS checkout helper. Development uses the current checkout, including local edits. +Installed hosts reuse the cached repository or clone `main` on first use. The child +installs dimOS from that checkout with `--with-editable`; its revision may differ +from the host's. Existing clones are not updated automatically. The checkout must +contain the declared project. Restart running modules after editing sources. -The sibling project's `.python-version` and `requires-python` select its Python +The project's `.python-version` and `requires-python` select its Python version. Environments are stored under the dimOS cache directory in `isolated-python//.venv`, so projects do not share environments. Preparation also warms the DimOS overlay before starting the readiness deadline. -For runtime sources shipped inside wheels, use a flat project with -`[tool.uv] package = false` and include its sources, manifest, and lockfile as package -data. Python imports the runtime from the project working directory, without an -editable build writing metadata into `site-packages`. Load models and download -checkpoints in the runtime's `start()`, keeping imports and construction lightweight. +Runtime projects are not packaged in dimOS wheels or source distributions. +The examples use `[tool.uv] package = false` and import runtime code from the +project working directory. Load models and download checkpoints in `start()`, +keeping imports and construction lightweight. The host contract retains the public module name and forwards contract RPCs to a unique internal endpoint. Ordinary dimOS serialization and transport handle RPC @@ -94,12 +92,9 @@ values, exceptions, timeouts, async methods, skills, streams, and module references. Restarting the contract starts a fresh interpreter and reloads the runtime package. -Nested Python projects own their runtime classes and tests. Host blueprint discovery -and pytest collection stop at directories containing another `pyproject.toml`. -Runtime classes can use public names without becoming host registry entries. -Run runtime tests explicitly with their project's pytest configuration and -`--confcutdir=.` to avoid loading the host's test fixtures. Keep test environments -outside `dimos/`, where repository source checks would otherwise scan dependencies. +Runtime classes and tests live outside `dimos/`, so host blueprint discovery and +source checks do not scan them. Run runtime tests with their project's pytest +configuration and `--confcutdir=.` to avoid loading host fixtures. ## Example @@ -114,14 +109,14 @@ restart behavior, and automatic shutdown. ## Runtime development -Root pytest and mypy checks exclude isolated projects. Run their tests and type +Root pytest and mypy check `dimos/`; isolated projects live outside that tree. Run their tests and type checks inside their own environment. For GraspGenX, from the repository root: ```bash -cd dimos/manipulation/grasping/grasp_gen_x/python +cd native/python/graspgenx export UV_PROJECT_ENVIRONMENT="${XDG_CACHE_HOME:-$HOME/.cache}/dimos/graspgenx-tests" -uv run --frozen --group tests --with-editable ../../../../.. python -m pytest -uv run --frozen --group lint --with-editable ../../../../.. python -m mypy +uv run --frozen --group tests --with-editable ../../.. python -m pytest +uv run --frozen --group lint --with-editable ../../.. python -m mypy ``` The tests mock the model backend and need no GPU or checkpoints. Runtime mypy diff --git a/dimos/experimental/isolated_python/example/contract.py b/dimos/experimental/isolated_python/example/contract.py index 5883813ca3..b8b4a66401 100644 --- a/dimos/experimental/isolated_python/example/contract.py +++ b/dimos/experimental/isolated_python/example/contract.py @@ -38,6 +38,7 @@ class Config(IsolatedPythonModuleConfig): class ExampleExternal(IsolatedPythonModule): """Multiply incoming integers in an isolated Python environment.""" + project_dir = "native/python/example" implementation = "example_external.runtime:ExampleExternalRuntime" config: Config diff --git a/dimos/experimental/isolated_python/module.py b/dimos/experimental/isolated_python/module.py index de9d5d9ef5..b3f12318f4 100644 --- a/dimos/experimental/isolated_python/module.py +++ b/dimos/experimental/isolated_python/module.py @@ -12,117 +12,48 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Run a concrete Module subclass in an isolated sibling Python project.""" +"""Run a concrete Module subclass in an isolated repository Python project.""" from __future__ import annotations from hashlib import sha256 -from importlib.metadata import PackageNotFoundError, distribution -import inspect -import json import os from pathlib import Path import pickle import select import subprocess -import sys import threading import time from typing import Any, ClassVar -from urllib.parse import urlencode, urlsplit -from packaging.utils import canonicalize_name - -if sys.version_info >= (3, 11): - import tomllib -else: - import tomli as tomllib - -from dimos.constants import CACHE_DIR, DIMOS_PROJECT_ROOT +from dimos.constants import CACHE_DIR from dimos.core.core import rpc from dimos.core.module import Module from dimos.core.native_module import NativeModule, NativeModuleConfig from dimos.core.rpc_client import RPCClient +from dimos.utils.data import get_project_root from dimos.utils.generic import short_id from dimos.utils.logging_config import setup_logger logger = setup_logger() -def _installed_dimos_requirement() -> str: - """Reuse the host's exact installed release or Git commit.""" - try: - installed = distribution("dimos") - except PackageNotFoundError as error: - raise RuntimeError( - "Cannot select isolated dimOS source: no dimos installation metadata; " - "use a source checkout or install dimOS from PyPI or Git" - ) from error - recorded = installed.read_text("direct_url.json") - if recorded is None: - return f"dimos=={installed.version}" - try: - origin = json.loads(recorded) - url = origin["url"] - vcs = origin["vcs_info"] - commit = vcs["commit_id"] - if ( - vcs["vcs"] != "git" - or not isinstance(url, str) - or not urlsplit(url).scheme - or not isinstance(commit, str) - or not commit - or "archive_info" in origin - or "dir_info" in origin - ): - raise ValueError("expected a Git URL and resolved commit") - source = f"git+{url}@{commit}" - if "subdirectory" in origin: - subdirectory = origin["subdirectory"] - if not isinstance(subdirectory, str) or not subdirectory: - raise ValueError("invalid project subdirectory") - source += "#" + urlencode({"subdirectory": subdirectory}) - return f"dimos @ {source}" - except (ValueError, KeyError, TypeError) as error: - raise RuntimeError( - "Cannot select isolated dimOS source: invalid or unsupported direct_url.json; " - "use a source checkout or install dimOS from PyPI or Git" - ) from error - - -def _resolve_dimos_source() -> tuple[str, str]: - """Select the imported checkout, or the installed distribution's provenance.""" - root = DIMOS_PROJECT_ROOT.resolve() - manifest = root / "pyproject.toml" - if manifest.is_file(): - with manifest.open("rb") as stream: - metadata = tomllib.load(stream) - name = metadata.get("project", {}).get("name") - if isinstance(name, str) and canonicalize_name(name) == "dimos": - return "--with-editable", str(root) - return "--with", _installed_dimos_requirement() - - -def _python_run_command(project: Path, dimos_source: tuple[str, str], *command: str) -> list[str]: +def isolated_python_run_command(project: Path, *command: str) -> list[str]: + """Run a project with dimOS from the shared source checkout.""" args = ["uv", "run"] if (project / "uv.lock").is_file(): args.append("--frozen") - args.extend(dimos_source) + args.extend(("--with-editable", str(get_project_root()))) args.extend(command) if (project / "pixi.toml").is_file(): return ["pixi", "run", "--executable", *args] return args -def isolated_python_run_command(project: Path, *command: str) -> list[str]: - """Run a command with the host DimOS available in an isolated project.""" - return _python_run_command(project, _resolve_dimos_source(), *command) - - class IsolatedPythonModuleConfig(NativeModuleConfig): """Process settings for an isolated Python module.""" - # Isolated Python modules resolve their real command from the sibling project. + # Isolated Python modules resolve their real command from the repository project. executable: str = "uv" startup_timeout: float = 30.0 output_limit: int = 64 * 1024 @@ -139,26 +70,25 @@ class IsolatedPythonModule(NativeModule): """A host RPC contract implemented by an isolated Python subclass. Contract classes set :attr:`implementation` to an import reference in a - sibling ``python/`` project. Calls to RPCs introduced by the contract are + repository project selected by :attr:`project_dir`. Contract RPCs are forwarded to the concrete runtime subclass. Framework and lifecycle RPCs remain on the host facade. """ config: IsolatedPythonModuleConfig implementation: ClassVar[str] + project_dir: ClassVar[str] _isolated_python_runtime: bool _runtime_client: RPCClient | None _runtime_name: str | None _module_refs: dict[str, RPCClient] - _dimos_source: tuple[str, str] | None def __init__(self, _isolated_python_runtime: bool = False, **kwargs: Any) -> None: self._isolated_python_runtime = _isolated_python_runtime self._runtime_client = None self._runtime_name = None self._module_refs = {} - self._dimos_source = None super().__init__(**kwargs) def __getattribute__(self, name: str) -> Any: @@ -177,12 +107,11 @@ def __getattribute__(self, name: str) -> Any: @property def runtime_project(self) -> Path: - source = Path(inspect.getfile(type(self))).resolve() - project = source.parent / "python" + project = get_project_root() / self.project_dir if not project.is_dir(): raise FileNotFoundError( f"Isolated Python runtime project is missing: {project}; " - "create a sibling 'python/' directory" + "ensure the shared dimOS checkout contains this project" ) if not (project / "pyproject.toml").is_file(): raise FileNotFoundError( @@ -203,7 +132,8 @@ def _prepare_command(self) -> list[str]: return self._uv_command(*args) def _launch_command(self, handshake_fd: int) -> list[str]: - return self._run_command( + return isolated_python_run_command( + self.runtime_project, "python", "-m", "dimos.experimental.isolated_python.bootstrap", @@ -217,11 +147,6 @@ def _launch_command(self, handshake_fd: int) -> list[str]: str(handshake_fd), ) - def _run_command(self, *command: str) -> list[str]: - if self._dimos_source is None: - self._dimos_source = _resolve_dimos_source() - return _python_run_command(self.runtime_project, self._dimos_source, *command) - def _new_runtime_name(self) -> str: public_name = self.config.instance_name or type(self).__name__ self._runtime_name = f"__isolated_python__/{public_name}/{short_id()}" @@ -240,11 +165,10 @@ def _runtime_env(self) -> dict[str, str]: return env def _run_prepare(self) -> None: - self._dimos_source = _resolve_dimos_source() # Resolve the DimOS overlay too, before the runtime's readiness deadline. commands = [ self._prepare_command(), - self._run_command("python", "-c", "pass"), + isolated_python_run_command(self.runtime_project, "python", "-c", "pass"), ] for command in commands: result = subprocess.run( diff --git a/dimos/experimental/isolated_python/test_module.py b/dimos/experimental/isolated_python/test_module.py index 2923f5e298..f5f27495bc 100644 --- a/dimos/experimental/isolated_python/test_module.py +++ b/dimos/experimental/isolated_python/test_module.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from importlib.metadata import PackageNotFoundError -import json from pathlib import Path import subprocess @@ -26,9 +24,11 @@ IsolatedPythonModuleConfig, isolated_python_run_command, ) +from dimos.utils import data class Contract(IsolatedPythonModule): + project_dir = "native/python/test" implementation = "runtime:Runtime" config: IsolatedPythonModuleConfig @@ -37,213 +37,119 @@ def value(self) -> int: raise NotImplementedError -def test_sibling_project_is_required(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: - source = tmp_path / "contract.py" - source.touch() +@pytest.fixture +def project(tmp_path, monkeypatch): + runtime = tmp_path / Contract.project_dir + runtime.mkdir(parents=True) + (runtime / "pyproject.toml").touch() + (runtime / "uv.lock").touch() monkeypatch.setattr( - "dimos.experimental.isolated_python.module.inspect.getfile", lambda _: str(source) + "dimos.experimental.isolated_python.module.get_project_root", lambda: tmp_path ) + return runtime + + +def test_checkout_resolution_is_lazy(mocker): + root = mocker.patch("dimos.experimental.isolated_python.module.get_project_root") module = Contract() try: - with pytest.raises(FileNotFoundError, match="sibling 'python/'"): + Contract.blueprint() + root.assert_not_called() + finally: + module.stop() + + +@pytest.mark.parametrize("manifest", [False, True]) +def test_missing_runtime_reports_expected_path(tmp_path, monkeypatch, manifest): + project = tmp_path / Contract.project_dir + if manifest: + project.mkdir(parents=True) + monkeypatch.setattr( + "dimos.experimental.isolated_python.module.get_project_root", lambda: tmp_path + ) + module = Contract() + try: + with pytest.raises(FileNotFoundError, match=str(project)): module.runtime_project # noqa: B018 finally: module.stop() -def test_uv_lock_enables_frozen_commands(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: - source = tmp_path / "contract.py" - source.touch() - project = tmp_path / "python" - project.mkdir() +@pytest.mark.parametrize("installed", [False, True]) +def test_runtime_uses_shared_checkout(tmp_path, monkeypatch, installed): + checkout = tmp_path / "repo" + (checkout / ".git").mkdir(parents=True) + project = checkout / Contract.project_dir + project.mkdir(parents=True) (project / "pyproject.toml").touch() - (project / "uv.lock").touch() - checkout = tmp_path / "checkout" - checkout.mkdir() - (checkout / "pyproject.toml").write_text('[project]\nname = "dimos"\n') monkeypatch.setattr( - "dimos.experimental.isolated_python.module.inspect.getfile", lambda _: str(source) + data, "DIMOS_PROJECT_ROOT", tmp_path / "site-packages" if installed else checkout ) - monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", checkout) + monkeypatch.setattr(data, "_get_user_data_dir", lambda: tmp_path) + data.get_project_root.cache_clear() module = Contract() try: - command = module._launch_command(7) - - assert module._prepare_command() == ["uv", "sync", "--frozen"] - assert command[:5] == [ + assert module.runtime_project == project + assert isolated_python_run_command(project, "python") == [ "uv", "run", - "--frozen", "--with-editable", str(checkout), + "python", ] - assert "--python" not in command finally: module.stop() + data.get_project_root.cache_clear() -@pytest.mark.parametrize( - ("origin", "requirement"), - [ - (None, "dimos==0.0.14"), - ( - { - "url": "https://github.com/dimensionalOS/dimos.git", - "vcs_info": {"vcs": "git", "requested_revision": "main", "commit_id": "a" * 40}, - }, - "dimos @ git+https://github.com/dimensionalOS/dimos.git@" + "a" * 40, - ), - ( - { - "url": "ssh://git@example.com/repo.git", - "vcs_info": {"vcs": "git", "commit_id": "b" * 40}, - "subdirectory": "packages/dimos", - }, - "dimos @ git+ssh://git@example.com/repo.git@" - + "b" * 40 - + "#subdirectory=packages%2Fdimos", - ), - ], -) -def test_installed_host_follows_source(tmp_path, monkeypatch, mocker, origin, requirement): - monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", tmp_path) - metadata = mocker.patch("dimos.experimental.isolated_python.module.distribution") - metadata.return_value.version = "0.0.14" - metadata.return_value.read_text.return_value = None if origin is None else json.dumps(origin) - - command = isolated_python_run_command(tmp_path, "python", "-c", "pass") - - assert command == ["uv", "run", "--with", requirement, "python", "-c", "pass"] - metadata.assert_called_once_with("dimos") - metadata.return_value.read_text.assert_called_once_with("direct_url.json") - - -@pytest.mark.parametrize("version", ["0.0.14", "0.0.15rc1", "0.0.15.dev2", "0.0.14+custom.1"]) -def test_index_install_preserves_full_host_version(tmp_path, monkeypatch, mocker, version): - monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", tmp_path) - metadata = mocker.patch("dimos.experimental.isolated_python.module.distribution") - metadata.return_value.read_text.return_value = None - metadata.return_value.version = version - - assert isolated_python_run_command(tmp_path, "python") == [ - "uv", - "run", - "--with", - f"dimos=={version}", - "python", - ] - - -@pytest.mark.parametrize("name", ["dimos", "another-project"]) -def test_only_dimos_manifest_selects_editable_source(tmp_path, monkeypatch, mocker, name): - (tmp_path / "pyproject.toml").write_text(f'[project]\nname = "{name}"\n') - monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", tmp_path) - metadata = mocker.patch("dimos.experimental.isolated_python.module.distribution") - metadata.return_value.read_text.return_value = None - metadata.return_value.version = "0.0.14" - expected = { - "dimos": ["--with-editable", str(tmp_path)], - "another-project": ["--with", "dimos==0.0.14"], - } - - assert isolated_python_run_command(tmp_path, "python") == [ - "uv", - "run", - *expected[name], - "python", - ] - - -def test_missing_installation_metadata_has_actionable_error(tmp_path, monkeypatch, mocker): - monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", tmp_path) - mocker.patch( - "dimos.experimental.isolated_python.module.distribution", - side_effect=PackageNotFoundError("dimos"), - ) - - with pytest.raises(RuntimeError, match="no dimos installation metadata"): - isolated_python_run_command(tmp_path, "python") - - -@pytest.mark.parametrize( - "recorded", - [ - "not json", - "null", - "[]", - "{}", - '{"url": "relative/path", "vcs_info": {"vcs": "git", "commit_id": "abc"}}', - '{"url": "file:///tmp/dimos", "dir_info": {}, "archive_info": {}}', - '{"url": "https://example.com/repo", "vcs_info": {"vcs": "hg", "commit_id": "abc"}}', - '{"url": "https://example.com/repo", "vcs_info": {"vcs": "git"}}', - '{"url": "https://example.com/dimos", "dir_info": {}}', - '{"url": "file:///tmp/dimos.whl", "archive_info": {}}', - '{"url": "https://example.com/dimos.tar.gz", "archive_info": {}}', - '{"url": "file:///tmp/dimos", "dir_info": {}}', - '{"url": "https://example.com/repo", "vcs_info": {"vcs": "git", "commit_id": ""}}', - '{"url": "https://example.com/repo", "vcs_info": {"vcs": "git", "commit_id": "abc"}, "subdirectory": 42}', - ], -) -def test_invalid_installation_source_fails_without_index_fallback( - tmp_path, monkeypatch, mocker, recorded -): - monkeypatch.setattr("dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", tmp_path) - metadata = mocker.patch("dimos.experimental.isolated_python.module.distribution") - metadata.return_value.read_text.return_value = recorded - - with pytest.raises(RuntimeError, match="invalid or unsupported direct_url.json"): - isolated_python_run_command(tmp_path, "python", "-c", "pass") +def test_uv_lock_enables_frozen_commands(project): + module = Contract() + try: + assert module._prepare_command() == ["uv", "sync", "--frozen"] + assert module._launch_command(7)[:5] == [ + "uv", + "run", + "--frozen", + "--with-editable", + str(project.parents[2]), + ] + finally: + module.stop() -def test_pixi_supplies_uv_when_manifest_exists( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - source = tmp_path / "contract.py" - source.touch() - project = tmp_path / "python" - project.mkdir() - (project / "pyproject.toml").touch() +def test_pixi_supplies_uv_when_manifest_exists(project): (project / "pixi.toml").touch() - monkeypatch.setattr( - "dimos.experimental.isolated_python.module.inspect.getfile", lambda _: str(source) - ) module = Contract() try: - assert module._prepare_command() == ["pixi", "run", "--executable", "uv", "sync"] + assert module._prepare_command() == [ + "pixi", + "run", + "--executable", + "uv", + "sync", + "--frozen", + ] + assert module._launch_command(7)[:4] == ["pixi", "run", "--executable", "uv"] finally: module.stop() -def test_runtime_environment_uses_project_specific_cache( - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: +def test_runtime_environment_uses_project_specific_cache(project, tmp_path, monkeypatch): monkeypatch.setenv("VIRTUAL_ENV", "/parent/.venv") monkeypatch.setenv("UV_PYTHON", "3.10") monkeypatch.setenv("UV_PROJECT_ENVIRONMENT", "/parent/.venv") monkeypatch.setattr("dimos.experimental.isolated_python.module.CACHE_DIR", tmp_path / "cache") - project = tmp_path / "python" - project.mkdir() - (project / "pyproject.toml").touch() - monkeypatch.setattr( - "dimos.experimental.isolated_python.module.inspect.getfile", - lambda _: str(tmp_path / "contract.py"), - ) module = Contract(extra_env={"EXAMPLE_SETTING": "configured"}) try: env = module._runtime_env() - assert "VIRTUAL_ENV" not in env assert "UV_PYTHON" not in env assert Path(env["UV_PROJECT_ENVIRONMENT"]).is_relative_to(tmp_path / "cache") assert module._runtime_env()["UV_PROJECT_ENVIRONMENT"] == env["UV_PROJECT_ENVIRONMENT"] - other = tmp_path / "other" - (other / "python").mkdir(parents=True) - (other / "python/pyproject.toml").touch() - monkeypatch.setattr( - "dimos.experimental.isolated_python.module.inspect.getfile", - lambda _: str(other / "contract.py"), - ) + other = tmp_path / "native/python/other" + other.mkdir() + (other / "pyproject.toml").touch() + monkeypatch.setattr(Contract, "project_dir", "native/python/other") assert module._runtime_env()["UV_PROJECT_ENVIRONMENT"] != env["UV_PROJECT_ENVIRONMENT"] assert env["EXAMPLE_SETTING"] == "configured" finally: @@ -284,19 +190,6 @@ def test_runtime_build_skips_environment_preparation(mocker: MockerFixture) -> N module.stop() -@pytest.fixture -def project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path: - runtime = tmp_path / "python" - runtime.mkdir() - (runtime / "pyproject.toml").touch() - (runtime / "uv.lock").touch() - monkeypatch.setattr( - "dimos.experimental.isolated_python.module.inspect.getfile", - lambda _: str(tmp_path / "contract.py"), - ) - return runtime - - def test_preparation_warms_the_launch_environment(project: Path, mocker: MockerFixture) -> None: run = mocker.patch( "dimos.experimental.isolated_python.module.subprocess.run", @@ -310,6 +203,7 @@ def test_preparation_warms_the_launch_environment(project: Path, mocker: MockerF module._prepare_command(), isolated_python_run_command(project, "python", "-c", "pass"), ] + assert module._launch_command(7)[:5] == run.call_args.args[0][:5] for call in run.call_args_list: assert call.kwargs["cwd"] == project assert call.kwargs["env"] == module._runtime_env() @@ -317,32 +211,6 @@ def test_preparation_warms_the_launch_environment(project: Path, mocker: MockerF module.stop() -def test_launch_reuses_prepared_host_version_until_next_build(project, monkeypatch, mocker): - monkeypatch.setattr( - "dimos.experimental.isolated_python.module.DIMOS_PROJECT_ROOT", project.parent - ) - metadata = mocker.patch("dimos.experimental.isolated_python.module.distribution") - metadata.return_value.read_text.return_value = None - metadata.return_value.version = "0.0.14" - run = mocker.patch( - "dimos.experimental.isolated_python.module.subprocess.run", - return_value=subprocess.CompletedProcess([], 0, "", ""), - ) - module = Contract() - try: - module._run_prepare() - metadata.return_value.version = "0.0.15" - - expected = ["uv", "run", "--frozen", "--with", "dimos==0.0.14"] - assert run.call_args.args[0][:5] == expected - assert module._launch_command(7)[:5] == expected - - module._run_prepare() - assert module._launch_command(7)[:5] == ["uv", "run", "--frozen", "--with", "dimos==0.0.15"] - finally: - module.stop() - - @pytest.mark.parametrize("failure_stage", [0, 1]) def test_preparation_failure_prevents_launch( project: Path, mocker: MockerFixture, failure_stage: int diff --git a/dimos/manipulation/grasping/grasp_gen_x/module.py b/dimos/manipulation/grasping/grasp_gen_x/module.py index b10cd66046..7a608e7a90 100644 --- a/dimos/manipulation/grasping/grasp_gen_x/module.py +++ b/dimos/manipulation/grasping/grasp_gen_x/module.py @@ -92,6 +92,7 @@ class GraspGenXError(RuntimeError): class GraspGenXModule(IsolatedPythonModule, GraspGenSpec): """Grasp proposals implemented in a separately locked Python environment.""" + project_dir = "native/python/graspgenx" implementation = "graspgenx_runtime.runtime:GraspGenXRuntimeModule" config: GraspGenXConfig diff --git a/docs/capabilities/manipulation/xarm-grasp.md b/docs/capabilities/manipulation/xarm-grasp.md index c25ff75bd7..5649ec7d53 100644 --- a/docs/capabilities/manipulation/xarm-grasp.md +++ b/docs/capabilities/manipulation/xarm-grasp.md @@ -22,7 +22,8 @@ top; drive those with `dimos agent-send "..."`. GraspGenX requires Linux x86_64, a CUDA 12.8-compatible GPU, and `uv >=0.9.25`. The first launch prepares its isolated Python 3.12 environment and downloads the -checkpoints. Use a source checkout or Git install; PyPI compatibility is unresolved. +checkpoints. Runtime sources come from the development checkout or the shared +repository clone used by installed dimOS. To show the MuJoCo window with Rerun disabled: diff --git a/dimos/experimental/isolated_python/example/python/.gitignore b/native/python/example/.gitignore similarity index 100% rename from dimos/experimental/isolated_python/example/python/.gitignore rename to native/python/example/.gitignore diff --git a/dimos/experimental/isolated_python/example/python/.python-version b/native/python/example/.python-version similarity index 100% rename from dimos/experimental/isolated_python/example/python/.python-version rename to native/python/example/.python-version diff --git a/dimos/experimental/isolated_python/example/python/example_external/runtime.py b/native/python/example/example_external/runtime.py similarity index 95% rename from dimos/experimental/isolated_python/example/python/example_external/runtime.py rename to native/python/example/example_external/runtime.py index dcfb6cb9a7..26bdc85f9a 100644 --- a/dimos/experimental/isolated_python/example/python/example_external/runtime.py +++ b/native/python/example/example_external/runtime.py @@ -21,7 +21,7 @@ class ExampleExternalRuntime(ExampleExternal): - """Concrete implementation loaded in the sibling environment.""" + """Concrete implementation loaded in the isolated environment.""" def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) diff --git a/dimos/experimental/isolated_python/example/python/pyproject.toml b/native/python/example/pyproject.toml similarity index 100% rename from dimos/experimental/isolated_python/example/python/pyproject.toml rename to native/python/example/pyproject.toml diff --git a/dimos/experimental/isolated_python/example/python/uv.lock b/native/python/example/uv.lock similarity index 100% rename from dimos/experimental/isolated_python/example/python/uv.lock rename to native/python/example/uv.lock diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/backend.py b/native/python/graspgenx/graspgenx_runtime/backend.py similarity index 100% rename from dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/backend.py rename to native/python/graspgenx/graspgenx_runtime/backend.py diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/runtime.py b/native/python/graspgenx/graspgenx_runtime/runtime.py similarity index 100% rename from dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/runtime.py rename to native/python/graspgenx/graspgenx_runtime/runtime.py diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/test_runtime.py b/native/python/graspgenx/graspgenx_runtime/test_runtime.py similarity index 100% rename from dimos/manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/test_runtime.py rename to native/python/graspgenx/graspgenx_runtime/test_runtime.py diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml b/native/python/graspgenx/pyproject.toml similarity index 100% rename from dimos/manipulation/grasping/grasp_gen_x/python/pyproject.toml rename to native/python/graspgenx/pyproject.toml diff --git a/dimos/manipulation/grasping/grasp_gen_x/python/uv.lock b/native/python/graspgenx/uv.lock similarity index 100% rename from dimos/manipulation/grasping/grasp_gen_x/python/uv.lock rename to native/python/graspgenx/uv.lock diff --git a/pyproject.toml b/pyproject.toml index bbe217cb23..f1998a897c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,8 +27,6 @@ include = ["dimos*"] # (MANIFEST.in already prunes these from the sdist; this keeps their stray # .py modules out of the wheel too). exclude = [ - "dimos.experimental.isolated_python.example.python*", - "dimos.manipulation.grasping.grasp_gen_x.python*", "dimos.web.websocket_vis.node_modules*", "dimos.web.command-center-extension*", "dimos.web.dimos_interface.src*", @@ -51,16 +49,12 @@ exclude = [ "**/*.svg", "**/*.tcss", "**/*.pyi", - "manipulation/grasping/grasp_gen_x/python/pyproject.toml", - "manipulation/grasping/grasp_gen_x/python/uv.lock", - "manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/*.py", ] [tool.setuptools.exclude-package-data] # Trims data the recursive globs above would otherwise pull in: test fixtures # and the native (C++/nix) build tree, which is built separately. "dimos" = [ - "manipulation/grasping/grasp_gen_x/python/graspgenx_runtime/test_*.py", "agents/fixtures/**", "agents/mcp/fixtures/**", "mapping/google_maps/fixtures/**", @@ -123,7 +117,6 @@ dependencies = [ "pydantic", "python-dotenv", "packaging>=24.0", - "tomli>=2.0; python_version < '3.11'", "filelock>=3.20,<4", "annotation-protocol>=1.4.0", "lazy_loader", @@ -623,8 +616,7 @@ strict = true warn_unused_ignores = false untyped_calls_exclude = ["zenoh"] explicit_package_bases = true -# IsolatedPythonModule owns sibling python/ projects with separate environments. -exclude = "^dimos/models/Detic(/|$)|(^|/)python/|.*/test_.|.*/tool_.|.*/conftest.py*" +exclude = "^dimos/models/Detic(/|$)|.*/test_.|.*/tool_.|.*/conftest.py*" [[tool.mypy.overrides]] module = [ @@ -743,8 +735,8 @@ exclude_also = [ max_size_kb = 75 ignore = [ "uv.lock", - "dimos/experimental/isolated_python/example/python/uv.lock", - "dimos/manipulation/grasping/grasp_gen_x/python/uv.lock", + "native/python/example/uv.lock", + "native/python/graspgenx/uv.lock", "*/package-lock.json", "Cargo.lock", "*/Cargo.lock", diff --git a/uv.lock b/uv.lock index f79aaabd9b..891b7b40cb 100644 --- a/uv.lock +++ b/uv.lock @@ -32,7 +32,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-09-06T08:32:08.145661055Z" +exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. exclude-newer-span = "P7D" [options.exclude-newer-package] @@ -1759,7 +1759,6 @@ dependencies = [ { name = "terminaltexteffects" }, { name = "textual" }, { name = "textual-serve" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "toolz" }, { name = "typer" }, { name = "typing-extensions" }, @@ -2454,7 +2453,6 @@ requires-dist = [ { name = "textual", specifier = "==3.7.1" }, { name = "textual-serve", specifier = ">=1.1.1,<2" }, { name = "timm", marker = "extra == 'perception'", specifier = ">=1.0.15" }, - { name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0" }, { name = "toolz", specifier = ">=1.1.0" }, { name = "torchreid", marker = "extra == 'misc'", specifier = "==0.2.5" }, { name = "transformers", extras = ["torch"], marker = "extra == 'perception'", specifier = ">=4.53.0,<4.54" },