feat: in-house robot asset manager#2505
Conversation
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #2505 +/- ##
==========================================
+ Coverage 70.81% 70.89% +0.08%
==========================================
Files 862 868 +6
Lines 77475 77839 +364
Branches 6882 6958 +76
==========================================
+ Hits 54862 55187 +325
- Misses 20818 20853 +35
- Partials 1795 1799 +4
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 3 files with indirect coverage changes 🚀 New features to boost your workflow:
|
Greptile SummaryThis PR replaces Git LFS robot model bundles with a lightweight in-house asset manager (
Confidence Score: 4/5Safe to merge after addressing the _directory_fingerprint cost-before-cache-check ordering and the unguarded local-commit case in _checkout_ref. Two issues in the new asset layer affect every robot startup: _directory_fingerprint reads the full byte content of every mesh file in the package root on each render_urdf call — including calls that hit the rendered-URDF cache — because the key is computed before the existence check, making each session startup pay O(total package bytes) in disk I/O regardless of whether anything has changed. Additionally, _checkout_ref forcibly repoints a local branch to origin/ref even when the working tree is clean but has local commits ahead of origin, silently making those commits unreachable. dimos/robot/assets/processing.py (_directory_fingerprint / _generate_render_key ordering) and dimos/robot/assets/git_cache.py (_checkout_ref / _is_dirty for local commits) Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["RobotDescriptionSource\n(url, ref)"] -->|"__truediv__ (lazy)"| B["RobotDescriptionPath\n(relative_path stored,\nno checkout yet)"]
B -->|"str() / __fspath__()\ncalled by consumer"| C["RobotDescriptionSource\n.checkout_path()"]
C -->|"_checkout_path_cache\nnot set"| D["GitAssetCache.resolve()"]
D -->|FileLock| E{checkout_path\nexists?}
E -->|No| F["_clone_missing()\nRepo.clone_from + _checkout_ref"]
E -->|Yes| G["_refresh_cached()"]
G -->|is_dirty?| H["warn + skip update"]
G -->|clean| I["origin.fetch\n_checkout_ref -B ref origin/ref"]
F --> J["GitAssetCheckout.path"]
H --> J
I --> J
J --> K["absolute Path cached"]
K --> L["render_urdf()\n_generate_render_key\n→ _directory_fingerprint\n→ read_bytes all files"]
L --> M{rendered_urdf\nexists?}
M -->|Yes| N["return cached URDF"]
M -->|No| O["process_xacro / read URDF\nwrite to cache"]
O --> P["prepare_urdf_for_drake()\nDrake-specific cleanup"]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["RobotDescriptionSource\n(url, ref)"] -->|"__truediv__ (lazy)"| B["RobotDescriptionPath\n(relative_path stored,\nno checkout yet)"]
B -->|"str() / __fspath__()\ncalled by consumer"| C["RobotDescriptionSource\n.checkout_path()"]
C -->|"_checkout_path_cache\nnot set"| D["GitAssetCache.resolve()"]
D -->|FileLock| E{checkout_path\nexists?}
E -->|No| F["_clone_missing()\nRepo.clone_from + _checkout_ref"]
E -->|Yes| G["_refresh_cached()"]
G -->|is_dirty?| H["warn + skip update"]
G -->|clean| I["origin.fetch\n_checkout_ref -B ref origin/ref"]
F --> J["GitAssetCheckout.path"]
H --> J
I --> J
J --> K["absolute Path cached"]
K --> L["render_urdf()\n_generate_render_key\n→ _directory_fingerprint\n→ read_bytes all files"]
L --> M{rendered_urdf\nexists?}
M -->|Yes| N["return cached URDF"]
M -->|No| O["process_xacro / read URDF\nwrite to cache"]
O --> P["prepare_urdf_for_drake()\nDrake-specific cleanup"]
Reviews (4): Last reviewed commit: "Update readme.md" | Re-trigger Greptile |
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Problem
DimOS currently keeps several manipulation robot model bundles in Git LFS. That copies upstream robot description assets into this repo, makes updates manual, and makes CI brittle on runners that do not have those LFS objects available.
robot_descriptions.pysolves a related problem, but it is not the right main asset layer for DimOS:This PR keeps one Git-backed robot asset supply path for built-in and user-supplied robots.
pip install dimosinstalls code only; repos are resolved lazily when a concrete model path is used.Solution
Add a small in-house robot description source layer:
GitAssetCachefor fresh-when-safe Git checkouts under the platform user cache directory, indimos/robot_assets;RobotDescriptionSource, a lazy path-like source handle for an upstream repo/ref;xarm_repo / "xarm_description" / "urdf" / "xarm_device.urdf.xacro";dimos/robot/assets, while Drake-specific cleanup stays in Drake utilities.The asset code is self-contained under
dimos/robot/assetsand avoids an__init__.pyto match DimOS import policy.How to Test
Manual check for a human reviewer:
Open the Drake Meshcat URL printed in the logs. The xArm7 model should load and visualize correctly. The first run may clone
xarm_ros2into the platform user cache underdimos/robot_assets; derived URDFs should be written underdimos/robot_assets/derived.For daemon mode:
Contributor License Agreement