feat(scene): restore stable objects across restarts#78
Open
HeartLinked wants to merge 1 commit into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds an object-persistence (“warm restore”) layer to system/scene so stable SceneObjects can survive scene restarts and retain consistent object IDs, with per-map isolation via map_id.
Changes:
- Introduces a milvus-lite–backed
ObjectStorefor persisting/loading stable objects (partitioned bymap_id). - Adds warm-restore plumbing:
ObjectRegistry.restore_object(), service boot-time restore + shutdown close, and scene-graph builder upserts on rebuild. - Updates perception reconcile to re-bind warm-restored objects to fresh detections (class+pose gating) to prevent ID churn/duplicates; updates docs, scripts, and dependencies accordingly.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| system/scene/tests/test_persistence.py | Adds unit tests for restore/rebind behavior and ObjectStore round-trips. |
| system/scene/scripts/start.sh | Mounts host-persisted /data/robonix and wires env vars for object memory + map id. |
| system/scene/scene_service/state/object_registry.py | Adds restore_object() and documents cg_uuid/restored internal attributes. |
| system/scene/scene_service/service.py | Boots with warm restore from ObjectStore, wires embedder, closes store on shutdown. |
| system/scene/scene_service/scene_graph/builder.py | Persists stable objects each rebuild (off-thread) when persistence is enabled. |
| system/scene/scene_service/persistence.py | New milvus-lite persistence implementation with schema management + map scoping. |
| system/scene/scene_service/ingest/perception_concept_graphs.py | Adds embed_text() and rebinds warm-restored objects before minting new IDs. |
| system/scene/README.md | Documents new env vars and “Object memory (warm restore)” behavior. |
| system/scene/pyproject.toml | Adds pymilvus dependency (with version exclusion). |
| system/scene/docker/requirements.txt | Adds pymilvus dependency (with version exclusion) for container builds. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+42
to
+55
| def test_restore_object_counter_collision(): | ||
| """A new object of a restored class numbers after the highest restored id, | ||
| never reusing or colliding with it.""" | ||
| reg = ObjectRegistry() | ||
| reg.restore_object(_make_obj("scene.object.cup_005", "cup")) | ||
| reg.restore_object(_make_obj("scene.object.cup_002", "cup")) | ||
|
|
||
| new = reg.insert_object( | ||
| "cup", Pose3D(0, 0, 0), BBox3D(), 0.5, now=1.0 | ||
| ) | ||
| assert new.object_id == "scene.object.cup_006", new.object_id | ||
| # Restored objects are still present and addressable. | ||
| assert reg.get_object("scene.object.cup_005") is not None | ||
| print(" [PASS] test_restore_object_counter_collision") |
Comment on lines
+58
to
+64
| def test_restore_object_unparseable_id(): | ||
| """An id without a numeric suffix is stored but doesn't touch the counter.""" | ||
| reg = ObjectRegistry() | ||
| reg.restore_object(_make_obj("scene.object.weird", "thing")) | ||
| new = reg.insert_object("thing", Pose3D(0, 0, 0), BBox3D(), 0.5, now=1.0) | ||
| assert new.object_id == "scene.object.thing_001", new.object_id | ||
| print(" [PASS] test_restore_object_unparseable_id") |
Comment on lines
+42
to
+48
| def _sanitize_map_id(raw: Optional[str]) -> str: | ||
| cleaned = _MAP_ID_UNSAFE.sub("_", (raw or "").strip()) | ||
| return cleaned or "default" | ||
| # open_clip ViT-B-32 text/image features are 512-d (see | ||
| # ingest/perception_concept_graphs.py). Object image features already live in | ||
| # this space, so caption-text vectors are directly comparable for G3. | ||
| _DIM = 512 |
Comment on lines
+195
to
+207
| def _embed_captions(self, captions: list[str]) -> list[list[float]]: | ||
| """Embed caption strings, falling back to the placeholder vector on a | ||
| missing or failing embedder so a write never blocks on the model.""" | ||
| if self._embed is None: | ||
| return [_PLACEHOLDER_VEC] * len(captions) | ||
| try: | ||
| vecs = self._embed(captions) | ||
| except Exception as e: # noqa: BLE001 | ||
| log.warning("[scene-persist] embed failed, using placeholder: %s", e) | ||
| vecs = None | ||
| if not vecs or len(vecs) != len(captions): | ||
| return [_PLACEHOLDER_VEC] * len(captions) | ||
| return vecs |
Comment on lines
+67
to
+78
| def test_restore_object_clears_cg_uuid_and_flags_restored(): | ||
| """Restore drops the stale per-process concept-graphs uuid and flags the | ||
| record `restored`, so the perception reconcile won't evict it before a live | ||
| detection re-binds it.""" | ||
| reg = ObjectRegistry() | ||
| obj = _make_obj("scene.object.cup_005", "cup") | ||
| obj.attributes["cg_uuid"] = "stale-uuid-from-last-process" | ||
| reg.restore_object(obj) | ||
| got = reg.get_object("scene.object.cup_005") | ||
| assert got.attributes.get("restored") is True | ||
| assert "cg_uuid" not in got.attributes | ||
| print(" [PASS] test_restore_object_clears_cg_uuid_and_flags_restored") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.