inspect-robots-unitree-g1 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. inspect_robots_unitree_g1-0.1.0/.env.example +5 -0
  2. inspect_robots_unitree_g1-0.1.0/.github/workflows/canary.yml +46 -0
  3. inspect_robots_unitree_g1-0.1.0/.github/workflows/ci.yml +183 -0
  4. inspect_robots_unitree_g1-0.1.0/.github/workflows/release.yml +81 -0
  5. inspect_robots_unitree_g1-0.1.0/.gitignore +33 -0
  6. inspect_robots_unitree_g1-0.1.0/.pre-commit-config.yaml +52 -0
  7. inspect_robots_unitree_g1-0.1.0/CITATION.cff +9 -0
  8. inspect_robots_unitree_g1-0.1.0/CLAUDE.md +44 -0
  9. inspect_robots_unitree_g1-0.1.0/LICENSE +21 -0
  10. inspect_robots_unitree_g1-0.1.0/PKG-INFO +265 -0
  11. inspect_robots_unitree_g1-0.1.0/README.md +236 -0
  12. inspect_robots_unitree_g1-0.1.0/plans/0001-g1-gr00t-design.md +436 -0
  13. inspect_robots_unitree_g1-0.1.0/pyproject.toml +131 -0
  14. inspect_robots_unitree_g1-0.1.0/src/inspect_robots_unitree_g1/CLAUDE.md +20 -0
  15. inspect_robots_unitree_g1-0.1.0/src/inspect_robots_unitree_g1/__init__.py +36 -0
  16. inspect_robots_unitree_g1-0.1.0/src/inspect_robots_unitree_g1/_unitree.py +27 -0
  17. inspect_robots_unitree_g1-0.1.0/src/inspect_robots_unitree_g1/config.py +347 -0
  18. inspect_robots_unitree_g1-0.1.0/src/inspect_robots_unitree_g1/embodiment.py +693 -0
  19. inspect_robots_unitree_g1-0.1.0/src/inspect_robots_unitree_g1/operator.py +60 -0
  20. inspect_robots_unitree_g1-0.1.0/src/inspect_robots_unitree_g1/packing.py +99 -0
  21. inspect_robots_unitree_g1-0.1.0/src/inspect_robots_unitree_g1/policy.py +331 -0
  22. inspect_robots_unitree_g1-0.1.0/src/inspect_robots_unitree_g1/preflight.py +78 -0
  23. inspect_robots_unitree_g1-0.1.0/src/inspect_robots_unitree_g1/py.typed +0 -0
  24. inspect_robots_unitree_g1-0.1.0/tests/conftest.py +96 -0
  25. inspect_robots_unitree_g1-0.1.0/tests/test_api_snapshot.py +30 -0
  26. inspect_robots_unitree_g1-0.1.0/tests/test_camera.py +42 -0
  27. inspect_robots_unitree_g1-0.1.0/tests/test_compat.py +73 -0
  28. inspect_robots_unitree_g1-0.1.0/tests/test_config.py +122 -0
  29. inspect_robots_unitree_g1-0.1.0/tests/test_conformance.py +16 -0
  30. inspect_robots_unitree_g1-0.1.0/tests/test_embodiment.py +460 -0
  31. inspect_robots_unitree_g1-0.1.0/tests/test_embodiment_docs.py +17 -0
  32. inspect_robots_unitree_g1-0.1.0/tests/test_eval_end_to_end.py +38 -0
  33. inspect_robots_unitree_g1-0.1.0/tests/test_operator.py +32 -0
  34. inspect_robots_unitree_g1-0.1.0/tests/test_packing.py +48 -0
  35. inspect_robots_unitree_g1-0.1.0/tests/test_policy.py +284 -0
  36. inspect_robots_unitree_g1-0.1.0/tests/test_preflight.py +58 -0
  37. inspect_robots_unitree_g1-0.1.0/tests/test_unitree.py +49 -0
  38. inspect_robots_unitree_g1-0.1.0/uv.lock +851 -0
@@ -0,0 +1,5 @@
1
+ # Optional overrides commonly used by local launch scripts.
2
+ GR00T_HOST=127.0.0.1
3
+ GR00T_PORT=5555
4
+ G1_IFACE=eth0
5
+ G1_CAMERA_ADDRESS=tcp://192.168.123.164:5556
@@ -0,0 +1,46 @@
1
+ name: Canary
2
+
3
+ # Weekly fresh-resolution test. Regular CI installs from uv.lock, so it can't
4
+ # see that a new release of an allowed dependency breaks us — this job can:
5
+ # it resolves the latest versions the pyproject ranges allow (ignoring the
6
+ # lockfile) and runs the test suite. On failure it opens an issue, like the
7
+ # red-main alert. A green canary is also the signal that it's safe to refresh
8
+ # the lockfile (`uv lock --upgrade`).
9
+ on:
10
+ schedule:
11
+ - cron: "23 5 * * 1" # Mondays 05:23 UTC
12
+ workflow_dispatch:
13
+
14
+ jobs:
15
+ fresh-deps:
16
+ name: fresh resolution (latest allowed deps)
17
+ runs-on: ubuntu-latest
18
+ permissions:
19
+ contents: read
20
+ issues: write # open the failure alert
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ with:
24
+ fetch-depth: 0 # hatch-vcs derives the version from git tags
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@v5
27
+ - name: Create venv
28
+ run: uv venv --python 3.12
29
+ - name: Install latest allowed deps (unlocked)
30
+ run: uv pip install -e ".[dev]"
31
+ - name: Pytest
32
+ run: uv run --no-sync pytest -q
33
+ - name: Open an issue on failure (unless one is already open)
34
+ if: ${{ failure() }}
35
+ env:
36
+ GH_TOKEN: ${{ github.token }}
37
+ RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
38
+ run: |
39
+ existing=$(gh issue list -R "$GITHUB_REPOSITORY" --state open --search "Canary in:title" --json number --jq length)
40
+ if [ "$existing" = "0" ]; then
41
+ gh issue create -R "$GITHUB_REPOSITORY" \
42
+ --title "Canary: latest allowed dependencies break the build" \
43
+ --body "The weekly fresh-resolution canary failed: $RUN_URL — a new release of an allowed dependency likely breaks us. Fix: cap the offending dependency in pyproject.toml (then uv lock), or adapt the code and refresh the lock with uv lock --upgrade."
44
+ else
45
+ echo "an open canary issue already exists; not filing another"
46
+ fi
@@ -0,0 +1,183 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ci-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ quality:
18
+ name: lint · type
19
+ runs-on: ubuntu-latest
20
+ timeout-minutes: 10
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v5
25
+ with:
26
+ enable-cache: true
27
+ cache-dependency-glob: |
28
+ **/pyproject.toml
29
+ **/uv.lock
30
+ - name: Install from uv.lock (dev extras)
31
+ run: uv sync --locked --python 3.11 --extra dev
32
+ - name: Ruff (lint)
33
+ run: uv run ruff check .
34
+ - name: Ruff (format check)
35
+ run: uv run ruff format --check .
36
+ - name: Mypy (strict)
37
+ run: uv run mypy
38
+
39
+ test:
40
+ name: test (${{ matrix.os }}, py${{ matrix.python-version }})
41
+ runs-on: ${{ matrix.os }}
42
+ timeout-minutes: 15
43
+ strategy:
44
+ fail-fast: false
45
+ matrix:
46
+ os: [ubuntu-latest, macos-latest]
47
+ python-version: ["3.11", "3.12"]
48
+ steps:
49
+ - uses: actions/checkout@v4
50
+ - name: Install uv
51
+ uses: astral-sh/setup-uv@v5
52
+ with:
53
+ enable-cache: true
54
+ cache-dependency-glob: |
55
+ **/pyproject.toml
56
+ **/uv.lock
57
+ - name: Install from uv.lock (dev extras)
58
+ run: uv sync --locked --python ${{ matrix.python-version }} --extra dev
59
+ - name: Pytest (100% coverage)
60
+ run: uv run pytest --cov --cov-report=term-missing
61
+
62
+ import-hygiene:
63
+ name: import with minimal dependencies
64
+ runs-on: ubuntu-latest
65
+ timeout-minutes: 10
66
+ steps:
67
+ - uses: actions/checkout@v4
68
+ - name: Install uv
69
+ uses: astral-sh/setup-uv@v5
70
+ with:
71
+ enable-cache: true
72
+ cache-dependency-glob: |
73
+ **/pyproject.toml
74
+ **/uv.lock
75
+ - name: Install package with only locked core pins
76
+ run: |
77
+ uv venv --python 3.12
78
+ uv pip install --no-deps .
79
+ uv export --locked --no-emit-project --no-hashes \
80
+ | grep -E '^(inspect-robots|numpy)==' > /tmp/minimal-locked.txt
81
+ uv pip install -r /tmp/minimal-locked.txt
82
+ - name: Assert integrations absent, then import
83
+ run: |
84
+ .venv/bin/python - <<'PY'
85
+ import importlib.util
86
+ for mod in ("zmq", "msgpack", "msgpack_numpy", "cv2", "unitree_sdk2py", "torch"):
87
+ assert importlib.util.find_spec(mod) is None, f"{mod} unexpectedly installed"
88
+ import inspect_robots_unitree_g1
89
+ print(
90
+ "inspect_robots_unitree_g1",
91
+ inspect_robots_unitree_g1.__version__,
92
+ "imported with minimal deps",
93
+ )
94
+ PY
95
+
96
+ gr00t-seam:
97
+ name: pinned Isaac-GR00T protocol seam
98
+ runs-on: ubuntu-latest
99
+ timeout-minutes: 15
100
+ steps:
101
+ - uses: actions/checkout@v4
102
+ - name: Install uv
103
+ uses: astral-sh/setup-uv@v5
104
+ - name: Fetch and verify pinned protocol files
105
+ env:
106
+ # verified 2026-07-17 against main
107
+ ISAAC_GR00T_REF: 9c7e746b2cd37a810070a98ef41d290a07e806c2
108
+ run: |
109
+ base="https://raw.githubusercontent.com/NVIDIA/Isaac-GR00T/$ISAAC_GR00T_REF"
110
+ curl --fail --silent --show-error "$base/gr00t/policy/server_client.py" -o /tmp/server_client.py
111
+ curl --fail --silent --show-error "$base/gr00t/policy/policy.py" -o /tmp/policy.py
112
+ curl --fail --silent --show-error "$base/gr00t/policy/gr00t_policy.py" -o /tmp/gr00t_policy.py
113
+ curl --fail --silent --show-error "$base/gr00t/configs/data/embodiment_configs.py" -o /tmp/embodiment_configs.py
114
+ python - <<'PY'
115
+ from pathlib import Path
116
+
117
+ checks = {
118
+ "/tmp/server_client.py": [
119
+ 'request: dict = {"endpoint": endpoint}',
120
+ '"get_action"',
121
+ "msgpack.packb",
122
+ "msgpack.unpackb",
123
+ "tuple(response)",
124
+ "zmq.error.Again",
125
+ "_init_socket",
126
+ "RCVTIMEO",
127
+ ],
128
+ "/tmp/policy.py": ["def get_action", "check_observation"],
129
+ "/tmp/gr00t_policy.py": ["decode_action", '["video", "state", "language"]'],
130
+ "/tmp/embodiment_configs.py": [
131
+ '"unitree_g1_full_body_with_waist_height_nav_cmd"',
132
+ '"annotation.human.task_description"',
133
+ '"ego_view"',
134
+ '"left_leg"',
135
+ ],
136
+ }
137
+ for path, needles in checks.items():
138
+ text = Path(path).read_text()
139
+ for needle in needles:
140
+ assert needle in text, f"{path} lost {needle!r}"
141
+ PY
142
+ - name: Import lazy wire dependencies
143
+ run: |
144
+ uv venv --python 3.12
145
+ uv pip install pyzmq msgpack msgpack-numpy
146
+ uv run --no-sync python - <<'PY'
147
+ import msgpack
148
+ import msgpack_numpy
149
+ import zmq
150
+ PY
151
+
152
+ ci-ok:
153
+ name: ci-ok
154
+ if: ${{ always() }}
155
+ needs: [quality, test, import-hygiene, gr00t-seam]
156
+ runs-on: ubuntu-latest
157
+ steps:
158
+ - name: Fail unless every needed job succeeded
159
+ env:
160
+ RESULTS: ${{ toJSON(needs) }}
161
+ run: echo "$RESULTS" | jq -e 'all(.[]; .result == "success")'
162
+
163
+ alert-red-main:
164
+ name: alert on red main
165
+ if: ${{ failure() && github.event_name == 'push' }}
166
+ needs: [quality, test, import-hygiene, gr00t-seam]
167
+ runs-on: ubuntu-latest
168
+ permissions:
169
+ issues: write
170
+ steps:
171
+ - name: Open an issue unless one is already open
172
+ env:
173
+ GH_TOKEN: ${{ github.token }}
174
+ RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
175
+ run: |
176
+ existing=$(gh issue list -R "$GITHUB_REPOSITORY" --state open --search "CI is red on main in:title" --json number --jq length)
177
+ if [ "$existing" = "0" ]; then
178
+ gh issue create -R "$GITHUB_REPOSITORY" \
179
+ --title "CI is red on main (${GITHUB_SHA:0:8})" \
180
+ --body "CI failed on a push to main: $RUN_URL. Stop the line: fix forward or revert before merging anything else. If the failure is transient, re-run the failed jobs and close this issue."
181
+ else
182
+ echo "an open red-main issue already exists; not filing another"
183
+ fi
@@ -0,0 +1,81 @@
1
+ name: Release
2
+
3
+ # Two ways to release; both publish to PyPI via trusted publishing (OIDC):
4
+ #
5
+ # 1. One-click: Actions -> Release -> "Run workflow" -> pick a version bump.
6
+ # The version is derived from git tags by hatch-vcs, so the cut job only
7
+ # computes the next tag and creates the GitHub release — nothing is
8
+ # committed or pushed to main (main is PR-only). Publish then runs in
9
+ # this same workflow run. (A release created with the built-in
10
+ # GITHUB_TOKEN never re-triggers workflows, which is why publish must
11
+ # live in this run rather than firing on the release event.)
12
+ #
13
+ # 2. Manual: publish a GitHub release by hand — the publish job runs on the
14
+ # release event. The tag must point at a commit that contains this file.
15
+ on:
16
+ release:
17
+ types: [published]
18
+ workflow_dispatch:
19
+ inputs:
20
+ bump:
21
+ description: "Version bump (applied to the latest v* tag)"
22
+ type: choice
23
+ options: [patch, minor, major]
24
+ default: patch
25
+
26
+ concurrency:
27
+ group: release
28
+ cancel-in-progress: false
29
+
30
+ jobs:
31
+ cut:
32
+ if: github.event_name == 'workflow_dispatch'
33
+ runs-on: ubuntu-latest
34
+ permissions:
35
+ contents: write # create the tag + release
36
+ outputs:
37
+ tag: ${{ steps.bump.outputs.tag }}
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+ with:
41
+ fetch-depth: 0 # full tag history to find the latest version
42
+ - name: Compute next tag from latest v* tag
43
+ id: bump
44
+ env:
45
+ BUMP: ${{ inputs.bump }}
46
+ run: |
47
+ latest=$(git tag --list 'v*' --sort=-v:refname | head -1)
48
+ latest=${latest:-v0.0.0}
49
+ IFS=. read -r major minor patch <<< "${latest#v}"
50
+ case "$BUMP" in
51
+ major) major=$((major+1)); minor=0; patch=0 ;;
52
+ minor) minor=$((minor+1)); patch=0 ;;
53
+ patch) patch=$((patch+1)) ;;
54
+ esac
55
+ echo "tag: $latest -> v$major.$minor.$patch"
56
+ echo "tag=v$major.$minor.$patch" >> "$GITHUB_OUTPUT"
57
+ - name: Create GitHub release
58
+ env:
59
+ GH_TOKEN: ${{ github.token }}
60
+ TAG: ${{ steps.bump.outputs.tag }}
61
+ run: gh release create "$TAG" --target "$GITHUB_SHA" --title "$TAG" --generate-notes
62
+
63
+ publish:
64
+ # Directly on manual release events; after cut on workflow_dispatch.
65
+ needs: [cut]
66
+ if: ${{ !cancelled() && (github.event_name == 'release' || needs.cut.result == 'success') }}
67
+ runs-on: ubuntu-latest
68
+ environment: pypi
69
+ permissions:
70
+ id-token: write # OIDC token for PyPI trusted publishing
71
+ steps:
72
+ - uses: actions/checkout@v4
73
+ with:
74
+ ref: ${{ needs.cut.outputs.tag || github.ref }}
75
+ fetch-depth: 0 # hatch-vcs derives the version from git tags
76
+ - name: Install uv
77
+ uses: astral-sh/setup-uv@v5
78
+ - name: Build sdist + wheel
79
+ run: uv build
80
+ - name: Publish to PyPI
81
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,33 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+
9
+ # Virtualenvs
10
+ .venv/
11
+ venv/
12
+
13
+ # Test / coverage
14
+ .pytest_cache/
15
+ .coverage
16
+ coverage.xml
17
+ htmlcov/
18
+ .mypy_cache/
19
+ .ruff_cache/
20
+ .uv-cache/
21
+
22
+ # Secrets / env
23
+ .env
24
+ .env.*
25
+ !.env.example
26
+
27
+ # OS / editor
28
+ .DS_Store
29
+ .idea/
30
+ .vscode/
31
+
32
+ # Agent worktrees
33
+ .worktrees/
@@ -0,0 +1,52 @@
1
+ # Pre-commit hooks for inspect-robots-unitree-g1. Install once per clone:
2
+ #
3
+ # uv run pre-commit install # sets up both pre-commit and pre-push hooks
4
+ #
5
+ # On commit: file hygiene + ruff (lint & format) + mypy (strict).
6
+ # On push: pytest with the 100% coverage gate.
7
+ #
8
+ # Local hooks run through `uv run` so they use this project's dev environment
9
+ # (matching CI) regardless of whether a virtualenv is activated. Requires uv.
10
+ default_install_hook_types: [pre-commit, pre-push]
11
+
12
+ repos:
13
+ - repo: https://github.com/pre-commit/pre-commit-hooks
14
+ rev: v5.0.0
15
+ hooks:
16
+ - id: trailing-whitespace
17
+ - id: end-of-file-fixer
18
+ - id: check-yaml
19
+ - id: check-toml
20
+ - id: check-merge-conflict
21
+ - id: check-added-large-files
22
+
23
+ - repo: local
24
+ hooks:
25
+ - id: ruff-check
26
+ name: ruff (lint, autofix)
27
+ entry: uv run ruff check --fix
28
+ language: system
29
+ types_or: [python, pyi]
30
+ require_serial: true
31
+
32
+ - id: ruff-format
33
+ name: ruff (format)
34
+ entry: uv run ruff format
35
+ language: system
36
+ types_or: [python, pyi]
37
+ require_serial: true
38
+
39
+ - id: mypy
40
+ name: mypy (strict)
41
+ entry: uv run mypy
42
+ language: system
43
+ types: [python]
44
+ pass_filenames: false
45
+
46
+ - id: pytest-coverage
47
+ name: pytest (100% coverage gate)
48
+ entry: uv run pytest --cov --cov-report=term-missing
49
+ language: system
50
+ always_run: true
51
+ pass_filenames: false
52
+ stages: [pre-push]
@@ -0,0 +1,9 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it as below."
3
+ title: "Inspect Robots Unitree G1: GR00T adapters for G1 arms"
4
+ authors:
5
+ - name: "Robocurve"
6
+ type: software
7
+ license: MIT
8
+ repository-code: "https://github.com/robocurve/inspect-robots-unitree-g1"
9
+ version: "0.1.0"
@@ -0,0 +1,44 @@
1
+ # inspect-robots-unitree-g1 agent guide
2
+
3
+ Inspect Robots adapters for Unitree G1 arms controlled through arm_sdk and
4
+ Isaac-GR00T policy servers.
5
+
6
+ ## The one big idea
7
+
8
+ The `g1_arms` embodiment and `gr00t` policy share one absolute 16-D
9
+ `joint_pos` contract. It contains seven left arm radians, one normalized left
10
+ hand, seven right arm radians, and one normalized right hand.
11
+
12
+ ## Layout
13
+
14
+ - `src/inspect_robots_unitree_g1/` contains the package.
15
+ - `tests/` contains injected hardware-free tests.
16
+ - `plans/0001-g1-gr00t-design.md` is the accepted binding design.
17
+
18
+ ## Working here
19
+
20
+ - Set `UV_CACHE_DIR=$PWD/.uv-cache` for uv commands.
21
+ - Run ruff check, ruff format check, strict mypy, and pytest with coverage.
22
+ - Keep optional SDK, ZMQ, msgpack, and OpenCV imports lazy.
23
+ - Keep 100 percent statement and branch coverage.
24
+
25
+ ## Safety invariants
26
+
27
+ - Weight is blended in while holding the measured pose before homing.
28
+ - Every reset reseeds the command baseline from measured arm joints.
29
+ - Every action is hard-clamped and speed-limited in the embodiment.
30
+ - Close opens hands, parks arms, blends weight to zero, then disconnects.
31
+ - Construction performs no hardware, network, camera, signal, or stdin work.
32
+ - Success reaches scoring only through `termination_reason="success"`.
33
+
34
+ ## CI and releases
35
+
36
+ - CI installs from `uv.lock`.
37
+ - `ci-ok` needs every blocking job.
38
+ - Versions come from git tags through hatch-vcs.
39
+
40
+ ## Writing style
41
+
42
+ - Do not use em dashes in prose.
43
+ - Do not use decorative emoji.
44
+ - Use plain headers without trailing colons.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RoboCurve
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.