memory2 tf service#2707
Conversation
Unify replay tf with the live service: StreamTF(MultiTBuffer, TFSpec) mirrors PubSubTF, pulling windows from a recorded tf stream on demand instead of receiving pushed messages. Lookups span buffer_size backward (or an explicit time_tolerance) plus forward_tolerance ahead; a cache miss prefetches cache_span past the query window and evicts everything first, so chronological replay costs one db query per cache_span. - TFLookup protocol (read side) + mypy conformance checks - get_pose hoisted from PubSubTF to TFSpec - MultiTBuffer: None tolerance resolves to buffer_size explicitly - map global: registration via tf stream (never Observation poses), --frame auto-detects world/map/odom via probe lookups, fail-fast when the cloud frame can't be resolved - tf lookup tests consolidated into a grid: live MultiTBuffer vs StreamTF over memory/sqlite stores
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
❌ 1 Tests Failed:
View the top 1 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
Greptile SummaryThis PR introduces
Confidence Score: 5/5Safe to merge; all changed paths are well-tested and the two observations raised are optimization notes without correctness impact. The core caching and eviction logic in StreamTF is correct for the single-threaded CLI use case, the world-frame detection handles all edge cases (null tf_buf, user-provided --frame, cloud already in a world frame), and the test suite now parametrizes over live, memory-backed, and sqlite-backed backends covering chain composition, time tolerance, eviction, and protocol conformance. The two open points — I/O under lock and redundant register calls — are non-blocking quality concerns. No files require special attention for correctness; dimos/memory2/tf.py and dimos/mapping/utils/cli/map.py carry the non-blocking quality notes. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant CLI as map.py CLI
participant STF as StreamTF
participant Store as SqliteStore / Stream
participant MTB as MultiTBuffer (cache)
CLI->>Store: stream("tf", TFMessage)
CLI->>STF: StreamTF.from_store(store)
CLI->>STF: "get("world", cloud_frame, ts=first_obs.ts)"
STF->>STF: _ensure(ts-10, ts+0)
STF->>Store: stream.at(center, radius)
Store-->>STF: TFMessage observations
STF->>MTB: receive_transform(...)
STF->>MTB: super().get(...)
MTB-->>STF: "Transform | None"
STF-->>CLI: world frame detected
loop for each obs in lidar (dedup)
CLI->>STF: register(obs) via _position()
STF->>STF: _ensure(obs.ts-10, obs.ts) — cache hit or evict+reload
STF->>MTB: super().get(world, frame_id, obs.ts)
MTB-->>STF: "Transform | None"
STF-->>CLI: "position tuple | None"
end
loop for each kept obs in _accumulate()
CLI->>STF: register(obs)
STF->>MTB: super().get(...) — typically cache hit
MTB-->>STF: Transform
STF-->>CLI: Transform (used to register cloud into world frame)
end
%%{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"}}}%%
sequenceDiagram
participant CLI as map.py CLI
participant STF as StreamTF
participant Store as SqliteStore / Stream
participant MTB as MultiTBuffer (cache)
CLI->>Store: stream("tf", TFMessage)
CLI->>STF: StreamTF.from_store(store)
CLI->>STF: "get("world", cloud_frame, ts=first_obs.ts)"
STF->>STF: _ensure(ts-10, ts+0)
STF->>Store: stream.at(center, radius)
Store-->>STF: TFMessage observations
STF->>MTB: receive_transform(...)
STF->>MTB: super().get(...)
MTB-->>STF: "Transform | None"
STF-->>CLI: world frame detected
loop for each obs in lidar (dedup)
CLI->>STF: register(obs) via _position()
STF->>STF: _ensure(obs.ts-10, obs.ts) — cache hit or evict+reload
STF->>MTB: super().get(world, frame_id, obs.ts)
MTB-->>STF: "Transform | None"
STF-->>CLI: "position tuple | None"
end
loop for each kept obs in _accumulate()
CLI->>STF: register(obs)
STF->>MTB: super().get(...) — typically cache hit
MTB-->>STF: Transform
STF-->>CLI: Transform (used to register cloud into world frame)
end
Reviews (4): Last reviewed commit: "Merge branch 'feat/ivan/memtf' of github..." | Re-trigger Greptile |
Covers the covered-range check, eviction, and reload atomically so a concurrent reader can't see a stale _covered against cleared buffers; also resets _covered before reloading so a failed _load can't leave the cache claiming coverage it evicted.
we converted recordings to sensor frame, mem now needs a tf service for global mapping