From 89f9431e7c86c3f4713f08017157ae124235842d Mon Sep 17 00:00:00 2001 From: Jeff Hykin Date: Mon, 27 Apr 2026 15:08:12 -0700 Subject: [PATCH 1/4] fix: rerun viewer not auto-opening due to serve_grpc deadlock register_colormap_annotation logs data via rr.log before serve_grpc starts the gRPC server, which deadlocks in the worker subprocess. Move rr.init before serve_grpc and colormap registration after. Also add missing local rerun imports after top-level imports were removed. --- dimos/visualization/rerun/bridge.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/dimos/visualization/rerun/bridge.py b/dimos/visualization/rerun/bridge.py index f6744e74fb..0c0e8dab25 100644 --- a/dimos/visualization/rerun/bridge.py +++ b/dimos/visualization/rerun/bridge.py @@ -33,14 +33,13 @@ from urllib.parse import urlparse from reactivex.disposable import Disposable -import rerun as rr from rerun._baseclasses import Archetype -import rerun.blueprint as rrb from rerun.blueprint import Blueprint from toolz import pipe # type: ignore[import-untyped] from dimos.core.core import rpc from dimos.core.module import Module, ModuleConfig +from dimos.msgs.sensor_msgs.PointCloud2 import register_colormap_annotation from dimos.protocol.pubsub.impl.lcmpubsub import LCM from dimos.protocol.pubsub.patterns import Glob, pattern_matches from dimos.protocol.pubsub.spec import SubscribeAllCapable @@ -53,7 +52,6 @@ RERUN_WEB_PORT, RerunOpenOption, ) -from dimos.visualization.rerun.init import rerun_init # TODO OUT visual annotations # @@ -133,6 +131,8 @@ def _hex_to_rgba(hex_color: str) -> int: def _with_graph_tab(bp: Blueprint) -> Blueprint: """Add a Graph tab alongside the existing viewer layout without changing it.""" + import rerun.blueprint as rrb + root = bp.root_container return rrb.Blueprint( rrb.Tabs( @@ -147,6 +147,9 @@ def _with_graph_tab(bp: Blueprint) -> Blueprint: def _default_blueprint() -> Blueprint: """Default blueprint with black background and raised grid.""" + import rerun as rr + import rerun.blueprint as rrb + return rrb.Blueprint( rrb.Spatial3DView( origin="world", @@ -244,11 +247,8 @@ def final_convert(msg: Any) -> RerunData | None: return msg.to_rerun() return None - def composed(msg: Any) -> RerunData | None: - return cast("RerunData | None", pipe(msg, *matches, final_convert)) - - self._override_cache[entity_path] = composed - return composed + # compose all converters + return lambda msg: pipe(msg, *matches, final_convert) def _get_entity_path(self, topic: Any) -> str: if self.config.topic_to_entity: @@ -259,6 +259,9 @@ def _get_entity_path(self, topic: Any) -> str: return f"{self.config.entity_prefix}{topic_str}" def _on_message(self, msg: Any, topic: Any) -> None: + """Handle incoming message - log to rerun.""" + import rerun as rr + entity_path: str = self._get_entity_path(topic) # Throttle entities with a max_hz limit @@ -282,6 +285,8 @@ def _on_message(self, msg: Any, topic: Any) -> None: @rpc def start(self) -> None: + import rerun as rr + super().start() logger.info("Rerun bridge starting") @@ -291,7 +296,7 @@ def start(self) -> None: entity: 1.0 / hz for entity, hz in self.config.max_hz.items() if hz > 0 } - rerun_init("dimos") + rr.init("dimos") parsed = urlparse(self.config.connect_url.replace("rerun+", "", 1)) grpc_port = parsed.port or RERUN_GRPC_PORT @@ -311,6 +316,8 @@ def start(self) -> None: ) logger.info(f"Rerun gRPC server ready at {server_uri}") + register_colormap_annotation("turbo") + if self.config.rerun_open not in get_args(RerunOpenOption): logger.warning( f"rerun_open was {self.config.rerun_open} which is not one of " @@ -402,6 +409,8 @@ def _log_connect_hints(self, grpc_port: int) -> None: logger.info("\n".join(lines)) def _log_static(self) -> None: + import rerun as rr + for entity_path, factory in self.config.static.items(): data = factory(rr) if isinstance(data, list): @@ -421,6 +430,8 @@ def log_blueprint_graph(self, dot_code: str, module_names: list[str]) -> None: dot_code: The DOT-format graph (from ``introspection.blueprint.dot.render``). module_names: List of module class names (to distinguish modules from channels). """ + import rerun as rr + try: result = subprocess.run( ["dot", "-Tplain"], input=dot_code, text=True, capture_output=True, timeout=30 From c4984e7fc8e3c591bb1ea66b918217400d502ad6 Mon Sep 17 00:00:00 2001 From: Jeff Hykin Date: Mon, 27 Apr 2026 16:11:15 -0700 Subject: [PATCH 2/4] Update dimos/visualization/rerun/bridge.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- dimos/visualization/rerun/bridge.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dimos/visualization/rerun/bridge.py b/dimos/visualization/rerun/bridge.py index 0c0e8dab25..c34c241abc 100644 --- a/dimos/visualization/rerun/bridge.py +++ b/dimos/visualization/rerun/bridge.py @@ -248,7 +248,9 @@ def final_convert(msg: Any) -> RerunData | None: return None # compose all converters - return lambda msg: pipe(msg, *matches, final_convert) + composed = lambda msg: pipe(msg, *matches, final_convert) + self._override_cache[entity_path] = composed + return composed def _get_entity_path(self, topic: Any) -> str: if self.config.topic_to_entity: From dcefeb2b3fc4757546af067d9d5d17d31f051681 Mon Sep 17 00:00:00 2001 From: Jeff Hykin Date: Mon, 27 Apr 2026 21:36:14 -0700 Subject: [PATCH 3/4] format --- dimos/visualization/rerun/bridge.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dimos/visualization/rerun/bridge.py b/dimos/visualization/rerun/bridge.py index c34c241abc..68166235c4 100644 --- a/dimos/visualization/rerun/bridge.py +++ b/dimos/visualization/rerun/bridge.py @@ -248,7 +248,9 @@ def final_convert(msg: Any) -> RerunData | None: return None # compose all converters - composed = lambda msg: pipe(msg, *matches, final_convert) + def composed(msg): + return pipe(msg, *matches, final_convert) + self._override_cache[entity_path] = composed return composed From 246743a1d122e2c805c8c265d07d2317d08a2328 Mon Sep 17 00:00:00 2001 From: Jeff Hykin Date: Mon, 27 Apr 2026 21:37:29 -0700 Subject: [PATCH 4/4] fix(types): annotate composed() in rerun bridge mypy --no-untyped-def flagged the inner closure. Annotate its msg/return and bind pipe()'s Any result to a typed local to drop the no-any-return leak. --- dimos/visualization/rerun/bridge.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dimos/visualization/rerun/bridge.py b/dimos/visualization/rerun/bridge.py index 68166235c4..8449d29693 100644 --- a/dimos/visualization/rerun/bridge.py +++ b/dimos/visualization/rerun/bridge.py @@ -248,8 +248,9 @@ def final_convert(msg: Any) -> RerunData | None: return None # compose all converters - def composed(msg): - return pipe(msg, *matches, final_convert) + def composed(msg: Any) -> RerunData | None: + result: RerunData | None = pipe(msg, *matches, final_convert) + return result self._override_cache[entity_path] = composed return composed