Skip to content

Pubsub pattern subs#1114

Merged
leshy merged 19 commits intodevfrom
feature/pubsub-pattern-subscriptions
Jan 29, 2026
Merged

Pubsub pattern subs#1114
leshy merged 19 commits intodevfrom
feature/pubsub-pattern-subscriptions

Conversation

@leshy
Copy link
Contributor

@leshy leshy commented Jan 26, 2026

LCM can now do

Topic(Glob("/sensor/*")),
Topic(Glob("/**/arm"), PoseStamped),
Topic(Glob("/**")),
Topic(re.compile(r"/sensor/.*")),
Topic(re.compile(r".*/arm"), PoseStamped),
Topic(re.compile(r".*/arm")),
Topic(re.compile(r".*/arm#geometry.*"))

And (bigger part) we have a spec/type structure now for any pubsub to support this. This is required for universal bridging

We need to write a rerun bridge asap

Intro

Point of this is to construct a generic bridging language, ability to define mappers from one protocol topic/msg to another.

This is easy - if you have a way to subscribe to all messages, this is what this PR lays the groundwork for

We need bridges mostly for spying/visualization - a rerun bridge atm

We want to do standard (and usually heaviest) OUT visualization related computation to be done outside of the modules themselves. Module OUTs can be tapped, then modules can rr.log individually only for specific specific fancy vis (projected pointcloud, planner algo rendering)

Rerun could be implemented as a funny (pub only) pubsub that can receive msgs that are automatically .to_rerun()-ed
.. or something like that

reorg

  • Move implementations to impl/ subdirectory (lcmpubsub, memory, shmpubsub, etc.)
  • Extract encoder mixins to encoders.py (PubSubEncoderMixin, PickleEncoderMixin, LCMEncoderMixin, JpegEncoderMixin) fix typing
  • Add grid tests for pattern matching subs test_pattern_sub.py
  • Add docs on how to grid test in general (useful for agents) docs/development/grid_testing.md

AllPubSub and DiscoveryPubSub

There are two ways in which a protocol can facilitate topic discovery, we implement base classes for both,

AllPubsub supports subscribing to all topics (Redis, LCM, MQTT)
DiscoveryPubSub supports discovering new topics (ROS)

These capabilities are orthogonal but they can implement one another and normalize the API

both implement subscribe_all function we care about.

Extending LCM

Topic class to support Regex or Glob patterns (as per the underlying proto)

TODO

other protocols need similar extensions (I will only do SHM),

we just want to get to something like subscribe_all (in potentially different ways) and once there we can bridge

once subscribe_all is reached, potentially post-reception (expensive) glob/regex implementations can also be implemented implicitly

Bridge

added preliminary bridge, mostly as a type spec to demonstrate the idea, shows how we could bridge different protocols once subscribe_all or glob/regex topics are implemented

Tests issue

Some tests are failing because some other test, lcm msgs are flying around while these tests are running. I'll investigate this, didn't want to sweep under rug, likely this is causing flakyness with other LCM tests also

- Move implementations to impl/ subdirectory (lcmpubsub, memory, shmpubsub, etc.)
- Extract encoder mixins to encoders.py (PubSubEncoderMixin, PickleEncoderMixin, LCMEncoderMixin, JpegEncoderMixin)
- Add AllPubSub and DiscoveryPubSub mixins with complementary default implementations
- Add GlobPubSub and RegexPubSub marker classes with docstring examples
- Update imports across codebase
- Add CLAUDE.md to .gitignore
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 26, 2026

Greptile Overview

Greptile Summary

This PR implements pattern-based subscriptions and bridging infrastructure for pubsub systems. The changes enable subscribing to topics using glob/regex patterns and facilitate cross-protocol message translation.

Major Changes:

  • Reorganized pubsub implementations into impl/ subdirectory
  • Extracted encoder mixins (PickleEncoderMixin, LCMEncoderMixin, JpegEncoderMixin) to encoders.py
  • Added AllPubSub and DiscoveryPubSub base classes to support subscribe_all() functionality
  • Implemented Glob pattern matching and regex support for LCM topics
  • Added Topic.from_channel_str() for parsing channel strings with type information
  • Created bridge.py module with Translator protocol and Bridge service for cross-protocol bridging
  • Added comprehensive grid tests in test_pattern_sub.py
  • Extracted resolve_msg_type() helper to dimos.msgs.helpers

Issues Found:

  • Type annotation bug in bridge() function where callback parameter types are swapped
  • Bridge.start() doesn't use the subscribe_topic config parameter

Confidence Score: 4/5

  • This PR is mostly safe to merge with 2 bugs that need fixing in the bridge module
  • The core refactoring and pattern subscription implementation is solid with comprehensive tests. However, there are 2 logic bugs in the new bridge.py module: incorrect type annotations in the callback and unused config parameter. These are straightforward fixes but could cause runtime issues if the bridge functionality is used before being fixed.
  • Pay close attention to dimos/protocol/pubsub/bridge.py - contains type annotation bug and unused config parameter

Important Files Changed

Filename Overview
dimos/protocol/pubsub/bridge.py New bridge module with type annotation issue in bridge() function - callback receives arguments in wrong order
dimos/protocol/pubsub/spec.py Refactored to extract base mixin and add AllPubSub/DiscoveryPubSub classes for subscribe-all pattern support
dimos/protocol/pubsub/encoders.py Extracted encoder mixins to separate module with clean separation of concerns
dimos/protocol/pubsub/impl/lcmpubsub.py New LCM implementation with Glob/regex pattern support and subscribe_all functionality
dimos/protocol/pubsub/test_pattern_sub.py Comprehensive grid tests for pattern-based subscriptions including glob and regex
dimos/msgs/helpers.py New helper to resolve message type names to classes, with caching for performance

Sequence Diagram

sequenceDiagram
    participant Source as Source PubSub<br/>(AllPubSub)
    participant Bridge as Bridge Service
    participant Translator as Translator
    participant Dest as Destination PubSub

    Note over Bridge: start()
    Bridge->>Source: subscribe_all(callback) or<br/>subscribe(topic_from, callback)
    Source-->>Bridge: unsubscribe function

    Note over Source: Message published
    Source->>Bridge: callback(msg_from, topic_from)
    Bridge->>Translator: topic(topic_from)
    Translator-->>Bridge: topic_to
    Bridge->>Translator: msg(msg_from)
    Translator-->>Bridge: msg_to
    Bridge->>Dest: publish(topic_to, msg_to)

    Note over Bridge: stop()
    Bridge->>Source: unsubscribe()
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

leshy added 9 commits January 26, 2026 15:05
- Add Glob class for glob-style pattern matching (*, **, ?)
- Topic now accepts str, re.Pattern, or Glob for flexible subscription patterns
- Pattern subscriptions return the actual matched channel in callback
- Remove duplicate LCMMsg and Topic from lcmservice.py (use DimosMsg)
- Add PubSubProtocol for structural typing
- Add tests for regex and glob pattern subscriptions
…subscriptions

- Add subscribe_all() method to LCMPubSubBase for subscribing to all topics
- Add Topic.from_channel_str() factory method to parse channel strings with embedded type info
- Channel format: /topic#module.ClassName enables automatic type extraction
- Add test for subscribe_all with typed message decoding
Move message type resolution logic to a dedicated helper module with
lru_cache for performance. Supports fallback to dimos_lcm module path.
@leshy leshy force-pushed the feature/pubsub-pattern-subscriptions branch from af9a79b to d077c67 Compare January 26, 2026 10:37
@leshy leshy changed the title WIP: Pubsub pattern subs Pubsub pattern subs Jan 26, 2026
@leshy leshy force-pushed the feature/pubsub-pattern-subscriptions branch from b86a7d0 to 3a0c089 Compare January 26, 2026 11:00
@leshy
Copy link
Contributor Author

leshy commented Jan 26, 2026

@greptileai can you review?

@dimensionalOS dimensionalOS deleted a comment from greptile-apps bot Jan 26, 2026
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

leshy added 4 commits January 26, 2026 20:05
- Fix AllPubSub type parameter order (TopicFrom, MsgFrom not MsgFrom, TopicFrom)
- Fix pass_msg callback signature to match spec (MsgFrom, TopicFrom)
- Pass subscribe_topic config to bridge() function
- helpers.py: Add type: ignore for getattr Any return
- lcmpubsub.py: Add type: ignore for callback type variance and mixin incompatibility
- shmpubsub.py: Add type: ignore for mixin incompatibility
- transport.py: Add arg-type to existing type: ignore
@paul-nechifor
Copy link
Contributor

So this works, but I think there's a certain bit of complexity with mixins and generics which isn't needed.

Just some brainstorming ideas

The way I view it there are 4 things:

  1. topics
  2. brokers
  3. publishers
  4. subscribers

Topics should contain every information about what is subscribed/published to and be just static data (do nothing).

One of the things I don't like about the current system is how I'm doing this currently for setting transports:

            ("detections", Detection3DModule): LCMTransport(
                "/detector3d/detections", Detection2DArray
            ),

LCMTransport constains LCM() in it's __init__. So it starts an LCMService just by specifying it. This means unitree_go2_blueprints.py starts a lot of LCM servers unnecessarely.

I think we should have something like:

PubSubProtocol = Literal["LCM", "SHM", ...]

@dataclass
class Topic
    protocol: PubSubProtocol
    topic: str # maybe better name lol
    type: RootMsgClass

We could have a singleton broker gateway which passes data to all real brokers. So you can have:

topic = Topic("LCM", '/color_image', Image)
Gateway.publish(topic, Image(...))

It checks if LcmBroker is started, if it's not, it starts it.

(For cleanup, we can have Gateway.clean_all() which stops everything, but simply calling Gateway.subscribe/publish cleanly restarts the relevant broker.)

This way, modules don't have to store their own LCMServers, or any server. They just have to know what topics they publish and what messages they publish.

Instead of the encoder being a mixin, it could just be a param. So we can have

Gateway.register_broker("LCM", LcmBroker()) # uses default encoder
Gateway.register_broker("pLCM", LcmBroker(encoder=PicleEncoder()))
Gateway.register_broker("JpegLCM", LcmBroker(encoder=JpegEncoder()))

It would be nice if Gateway.subscribe(topic, callback) returned a Disposable.


In/Out could have subscribe/publish which just uses the gateway


class In:
    ...
    def publish(self, msg):
        Gateway.publish(self.topic, msg)

    @property
    def topic(self):
        # the topic str and protocol gets set by the blueprint

# so that we could have:

class MyModule(Module):
    color_image = In(Image) # Instantiation, not type

    def func(self):
        self.color_image.publish(Image(...))


But then we can implement bridges at the Gateway level.

Gateway.register_bridge(LcmToRosBridge())
Gateway.register_bridge(RerunListener())

Subscribe all could be just:

Gateway.subscribe(Topic("LCM", None, None))

def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def]
super().__init__(*args, **kwargs)
self._encode_callback_map: dict = {} # type: ignore[type-arg]
def on_msg(msg: MsgT, topic: TopicT) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need locks on modifying seen.

import traceback
def subscribe_all(self, callback: Callable[[MsgT, TopicT], Any]) -> Callable[[], None]:
"""Subscribe to all topics by subscribing to each discovered topic."""
subscriptions: list[Callable[[], None]] = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a lock

@leshy
Copy link
Contributor Author

leshy commented Jan 27, 2026

One of the things I don't like about the current system is how I'm doing this currently for setting transports:

            ("detections", Detection3DModule): LCMTransport(
                "/detector3d/detections", Detection2DArray
            ),

LCMTransport constains LCM() in it's init. So it starts an LCMService just by specifying it. This means unitree_go2_blueprints.py starts a lot of LCM servers unnecessarely.

Yeah agreed, the reason I auto-started LCM was that it was early in dev and very cheap. Generally transports should be a Resource and they do have start() and stop() methods and are supposed to be called, until then they should be dead data, the rest of the system (blueprints?) was built with the assumption of auto-start of a Transport. we should just turn off start on init for LCM, I think maybe easy for you?

I think we should have something like:

PubSubProtocol = Literal["LCM", "SHM", ...]

@dataclass
class Topic
    protocol: PubSubProtocol
    topic: str # maybe better name lol
    type: RootMsgClass

So my stuff is more abstract then this because I didn't want to make assumptions pubsub being used for transport or what is required to configure a topic for a specific pubsub protocol

I imagined

# off site detections, local 3d projections
Detection3dModule.2d_detections.transport = DimosServerTransport("dimos.com")
# video specific transport
VideoParser.color_image.transport = CameraStream("rtsp://....")
# sending odom to remote server
robot.odom.transport = HTTPPost("http://bla.com/receiver.php")

Topic is just an init arg, and the concept of a Topic is not tied to Transport, and Topics if used in pubsub are not generalized since they not 1-to-1 across protocols, for example ROS Topic includes ROS specific QOS, buffer settings, zenoh topic might contain a broker IP address or node discovery mechanism etc. I can imagine a websocket connection to a dimos server being a Transport in the future, this requires a web server url.

Right now transport spec should change to have more clear naming of methods

class Transport():
    send(msg: MsgT): 
       ...
    subscribe(callback):
      ...

and that's all, so people don't feel tied to Pubsub as base for this

and the whole Stream class can be thrown away / rewritten, it's very hacky and complex

mariage

That being said, I think we can marry the two ideas, nothing stops you for having

VideoParser.color_image.transport = mybroker.topic("/something")

OR (what I did before)

# mytransport in the background uses a process-central broker
VideoParser.color_image.transport = mytransport(topic("/something"))

brief thoughts, what do you think?

@paul-nechifor
Copy link
Contributor

TL;DR: I think we should be able to specify how something is meant to be transported without reifying the transport.


I view a topic as the equivalent of a connection string for a database. (Maybe I should use a different name, not topic.)

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

def make_session(conn_url: str):
    engine = create_engine(
        conn_url,
        pool_size=10,
        max_overflow=20,
    )
    Session = sessionmaker(bind=engine)
    return Session()

You can call make_session("postgresql+psycopg2://user:pass@host/db") or make_session("mysql+pymysql://user:pass@host/db").

make_session doesn't care if it's connecting to a Postgres DB or a MySQL DB. Everything is encoded in the connection string.

So it's kinda like saying:

Detection3dModule.2d_detections.transport = 'lcm://Detection2D@/2d_detections'

But instead of using a string I break up the URI into its constituent parts.

Detection3dModule.2d_detections.transport = Topic("LCM", "/2d_detections", Detection2D)

Topic is just an init arg, and the concept of a Topic is not tied to Transport, and Topics if used in pubsub are not generalized since they not 1-to-1 across protocols, for example ROS Topic includes ROS specific QOS, buffer settings, zenoh topic might contain a broker IP address or node discovery mechanism etc.

The same is true for connection strings. A MySQL connection string can include charset, which makes no sense in Postgres:

make_session('mysql+pymysql://appuser:secret@db.example.com:3306/appdb?charset=utf8mb4')

In Postgres you'd use client_encoding=UTF8 for UTF8.

For us, we could have:

video_parser.color_image.transport = Topic("JpegSHM", '/color_image', Image, options={"quality": 75})

leshy added 4 commits January 29, 2026 16:09
…pnet

These third-party libraries are installed but missing py.typed markers,
causing mypy to raise import-untyped errors in addition to import-not-found.
- Add DecodingError exception for skipping messages in decode()
- Add LCMTopicProto protocol for type-safe LCM topic handling
- Filter LCM_SELF_TEST topic in both encoders and lcmpubsub handler
- Fix type annotations in shmpubsub and ros_bridge
…binding

- Use explicit re-export syntax in impl/__init__.py
- Bind loop variable in lambda default argument to fix B023
q.put_nowait(msg)

unsubscribe_fn = self.subscribe(topic, _cb)
unsubscribe_fn = self.subscribe(topic, _cb) # type: ignore[attr-defined]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come this # type: ignore is needed now?

@dataclass
class Topic:
topic: str | re.Pattern[str] | Glob
lcm_type: type[DimosMsg] | None = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about types for which we use pickling? Shouldn't this basically allow any class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes but this is a structural problem, I need to get rid of inheritance and switch to composition to have correct types and this sorted.. will do this later

lcm_subscription = self.l.subscribe(topic_str, lambda _, msg: callback(msg, topic))

# Set queue capacity to 10000 to handle high-volume bursts
lcm_subscription.set_queue_capacity(10000)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to make this a constant.

Copy link
Contributor

@paul-nechifor paul-nechifor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some comments on type: ignores? Maybe fix in a second pass?

@leshy leshy merged commit c4a7ec0 into dev Jan 29, 2026
15 checks passed
@leshy
Copy link
Contributor Author

leshy commented Jan 29, 2026

Just some comments on type: ignores? Maybe fix in a second pass?

taking in follow up PR just so this large thing doesn't dangle, follow ups coming

@leshy leshy mentioned this pull request Jan 29, 2026
spomichter added a commit that referenced this pull request Feb 21, 2026
Release v0.0.10: Manipulation Stack, MuJoCo Simulation, DDS Transport, Web and Native Visualization via Rerun


## Highlights

88+ commits, 20 contributors, 700+ files changed.

The TLDR: **a complete manipulation stack**, **MuJoCo simulation**, **DDS transport**, and **a rewritten visualization pipeline**. Agents are no longer bolted on top — they're refactored as native modules with direct stream access. The entire ROS message dependency has been removed from core DimOS, and we've added VR, phone, and arm teleoperation stacks. You can now vibecode a pick-and-place task from natural language to motor commands. Installation has been significantly streamlined — no more direnv, simpler setup, and the web viewer is now the default.

---

## 🚀 New Features

### Simulation
- **MuJoCo simulation module** — Run any DimOS blueprint in simulation with no hardware. Supports xArm and Unitree embodiments, parses MJCF/URDF for robot properties, monotonic clock timing (no `time.sleep`). `dimos --simulation run unitree-go2` ([#1035](#1035)) by @jca0
- **Simulation teleop blueprints** — Added simulation teleop blueprints for Piper, xArm6, and xArm7. ([#1308](#1308)) by @mustafab0

### Manipulation
- **Modular manipulation stack** — Full planning stack with Drake: FK/IK solvers (Jacobian + Drake optimization), RRT path planning, world model with obstacle monitoring, multi-robot management. xArm6/7 and Piper support. ([#1079](#1079)) by @mustafab0
- **Joint servo and cartesian controllers** — Joint position/velocity controllers and cartesian IK task with Pinocchio solver. PoseStamped stream input for real-time control. ([#1116](#1116)) by @mustafab0
- **GraspGen integration** — Grasp generation via Docker-hosted GPU model. Lazy container startup, thread-safe init, RPC `generate_grasps()` returns ranked PoseArray. ([#1119](#1119), [#1234](#1234)) by @JalajShuklaSS
- **Gripper control** — Gripper RPC methods on control coordinator, exposed adapter property for custom implementations. ([#1213](#1213)) by @mustafab0
- **Detection3D and Object support** — Object input topics, TF support on manipulation module, pointcloud-to-convex-hull for Drake imports. ([#1236](#1236)) by @mustafab0
- **Agentic pick and place** — Reimplemented manipulation skills for agent-driven pick-and-place workflows. ([#1237](#1237)) by @mustafab0

### Teleoperation
- **Quest VR teleoperation** — Full WebXR + Deno bridge stack. Quest controller data (pose, trigger, grip) streamed to DimOS modules. Monitor-style locking for control loops. ([#1215](#1215)) by @ruthwikdasyam
- **Phone teleoperation** — Control Go2 from your phone with a web-based teleop interface. ([#1280](#1280)) by @ruthwikdasyam
- **Arm teleop with Pinocchio IK** — Single and dual arm teleoperation using Pinocchio inverse kinematics. Blueprints for xArm, Piper, and dual configurations. ([#1246](#1246)) by @ruthwikdasyam

### Transports & Infrastructure
- **DDS transport protocol** — CycloneDDS transport with configurable QoS (high-throughput and reliable profiles). Optional install, benchmark integration. ([#1174](#1174)) by @Kaweees
- **Pubsub pattern subscriptions** — Glob and regex pattern matching for topic subscriptions. `subscribe_all()` for bridge-style consumers. Topic type encoding in channel strings (`/topic#module.ClassName`). ([#1114](#1114)) by @leshy
- **LCM raw bytes passthrough** — Skip `lcm_encode()` when message is already bytes. ([#1223](#1223)) by @leshy
- **Unified TimeSeriesStore** — Pluggable backends (InMemory, SQLite, Pickle, PostgreSQL) with SortedKeyList for O(log n) operations. Replaces the old replay system and TimestampedCollection. Collection API with slice, range, and streaming methods. ([#1080](#1080)) by @leshy
- **DimosROS benchmark tests** — Benchmark suite for ROS transport performance. ([#1087](#1087)) by @leshy

### Navigation
- **FASTLIO2 support** — Hardware-verified localization with arm64 support. Docker deployment with FAR Planner, terrain analysis, and bagfile playback mode. Builds or-tools from source on arm64. ([#1149](#1149)) by @baishibona
- **Native Livox + FASTLIO2 module** — First-class DimOS native module for Livox Mid-360 lidar with FASTLIO2 localization. ([#1235](#1235)) by @leshy

### Visualization
- **RerunBridge module and CLI** — New bridge that subscribes to all LCM messages and logs those with `to_rerun()` to Rerun viewer. GlobalConfig singleton, web viewer support. Replaces the old rerun initialization system. ([#1154](#1154)) by @leshy
- **Webcam rerun visualization** — Camera module logs to Rerun with pinhole projection for 3D visualization. ([#1117](#1117)) by @ruthwikdasyam
- **Default viewer switched to rerun-web** — Browser-based viewer is now the default for broader compatibility. No native viewer install needed. ([#1324](#1324)) by @spomichter

### Agents
- **Agent refactor** — Restructured agent module with cleaner imports and global config integration. ([#1211](#1211)) by @paul-nechifor
- **Timestamp knowledge** — Agents now have timestamp awareness in prompts for temporal reasoning. ([#1093](#1093)) by @ClaireBookworm
- **Observe skill** — Go2 can now observe (capture and describe) its environment via agent skill. ([#1109](#1109)) by @paul-nechifor

### Platform & Hardware
- **G1 without ROS** — Unitree G1 blueprints decoupled from ROS dependency. Lazy imports for fast startup. ([#1221](#1221)) by @jeff-hykin
- **ARM (aarch64) support** — DimOS runs on ARM hardware. Platform-conditional dependencies, open3d source builds for arm64. ([#1229](#1229)) by @jeff-hykin
- **Universal joint/hardware schema** — `HardwareComponent` dataclass with `JointState`, `JointName` type aliases. Backend registry with auto-discovery for SDK adapters. ([#1040](#1040), [#1067](#1067)) by @mustafab0

---

## 🔧 Improvements

- **Optional Dask** — Start without Dask using `--no-dask` flag. Startup time reduced from ~60s to ~45s. ([#1111](#1111), [#1232](#1232)) by @paul-nechifor
- **RPC rework** — Renamed `ModuleBlueprint` → `_BlueprintAtom`, `ModuleBlueprintSet` → `Blueprint`, `ModuleConnection` → `Stream`. Added `ModuleRef`, improved type hints throughout. ([#1143](#1143)) by @jeff-hykin
- **Image class simplification** — Rewritten as pure NumPy dataclass. Removed CUDA backend, unused methods (solve_pnp, csrt_tracker), and image_impls/ directory. ([#1161](#1161)) by @leshy
- **Odometry message cleanup** — Simplified Odometry message type. ([#1256](#1256)) by @leshy
- **Remove all ROS message dependencies** — Purged ROS message types from core DimOS. Refactored rosnav to use ROSTransport. Removed dead ROS bridge code. ([#1230](#1230)) by @alexlin2
- **Removed bad function serialization** — Eliminated unnecessary serialization of Python functions. ([#1121](#1121)) by @paul-nechifor
- **Benchmark IEC units** — Switched bandwidth benchmarks from SI to IEC units for accuracy. ([#1147](#1147)) by @leshy
- **Pubsub typing improvements** — Thread-safety locks on `subscribe_new_topics` and `subscribe_all`. Proper type params across pubsub stack. ([#1153](#1153)) by @leshy
- **Autogenerated blueprint list** — Blueprints are now auto-discovered and listed. ([#1100](#1100)) by @paul-nechifor
- **Generic Buttons message** — Renamed `QuestButtons` to `Buttons` with generic field names for cross-platform teleop. ([#1261](#1261)) by @ruthwikdasyam
- **Dev container uses ros-dev image** — `./bin/dev` now runs the ROS-enabled dev image. ([#1170](#1170)) by @leshy
- **LSP support** — Added python-lsp-server and python-lsp-ruff to dev dependencies. ([#1169](#1169)) by @leshy
- **Lazy-load pyrealsense2** — RealSense camera module uses lazy imports to avoid errors in simulation environments without the SDK. ([#1309](#1309)) by @spomichter
- **Removed unused mmcv and mmengine** — Dead Detic dependencies removed, eliminating slow source builds from install. ([#1319](#1319)) by @spomichter
- **Simplified installation** — Removed direnv requirement, streamlined install instructions across all platforms. ([#1315](#1315)) by @spomichter
- **DDS extra excluded from --all-extras** — `cyclonedds` requires a source build, so `dds` is now excluded from `uv sync --all-extras` by default. ([#1318](#1318)) by @spomichter
- **Nix pre-commit skip** — Skip pre-commit install if hooks already exist. ([#1162](#1162)) by @leshy
- **Removed base-requirements** — Consolidated dependency management. ([#1098](#1098)) by @paul-nechifor
- **Removed old graspnet** — Cleaned up deprecated graspnet version. ([#1248](#1248)) by @paul-nechifor
- **Code cleanup** — Removed `tofix` markers ([#1216](#1216)), fixed ruff issues ([#1112](#1112)), removed old README_installation.md ([#1101](#1101)) by @paul-nechifor

---

## 🐛 Bug Fixes

- Fix LFS updating (move from .local to venv) ([#1090](#1090)) by @jeff-hykin
- Launch hotfixes: git clone HTTPS, get_data main branch ([#1091](#1091)) by @spomichter
- Fix camera demo not showing in Rerun ([#1148](#1148)) by @jeff-hykin
- Default to rerun native viewer ([#1099](#1099)) by @Nabla7
- Fix exploration blocking agent loop ([#1258](#1258)) by @paul-nechifor
- Fix person-follow blocking agent loop ([#1278](#1278)) by @paul-nechifor
- Skip metric3d tests on unsupported xformers GPUs (Blackwell compute capability >9.0) ([#1225](#1225)) by @leshy
- Fix manipulation tests ([#1218](#1218), [#1247](#1247)) by @jeff-hykin, @paul-nechifor
- Fix control coordinator e2e test ([#1212](#1212)) by @mustafab0
- Fix xarm7-sim broken e2e tests ([#1294](#1294)) by @paul-nechifor
- Pin langchain to restore supported providers ([#1241](#1241)) by @spomichter
- Fix missing library dependencies in Nix flake ([#1240](#1240)) by @Kaweees
- Fix discord invite link ([#1122](#1122)) by @spomichter
- macOS edgecase fix ([#1096](#1096)) by @jeff-hykin
- Fix second N in logo ([#1250](#1250)) by @jeff-hykin
- Fix Unitree Go2 minor issues ([#1307](#1307)) by @paul-nechifor
- Fix broken tests ([#1305](#1305)) by @ruthwikdasyam
- Fix `uv sync` for some macOS systems ([#1322](#1322)) by @jeff-hykin
- Fix mmcv install ([#1313](#1313)) by @paul-nechifor
- Fix mypy issues ([#1150](#1150), [#1167](#1167), [#1257](#1257)) by @leshy, @paul-nechifor, @jeff-hykin
- Fix Nix install uv pip extras ([#1321](#1321)) by @spomichter

---

## 📚 Documentation

- **Major docs overhaul** — New README with feature grid, hardware table, quickstart. Navigation, transports, data streams, and agent docs. ([#1295](#1295)) by @leshy
- **Day 1 docs** — Comprehensive getting started guide, development docs, contributing guide, architecture overview. Executable blueprint docs via md-babel-py. ([#1064](#1064)) by @jeff-hykin
- **Arm integration guide** — How-to for integrating new robotic arms with DimOS. ([#1238](#1238)) by @mustafab0
- **MCP documentation update** — Updated MCP install and usage instructions. ([#1251](#1251)) by @Kaweees
- **Docker docs** — First pass on Docker deployment documentation. ([#1151](#1151)) by @leshy
- **Transports documentation** — Encode/decode mixins, SHM examples, ROS/DDS transport docs. ([#1107](#1107)) by @leshy
- **Rerun API examples** — Updated examples for the new RerunBridge API. ([#1262](#1262)) by @jeff-hykin
- **PR template** added ([#1172](#1172)) by @christiefhyang
- **Simplified install instructions** — Removed direnv, streamlined across all platforms. ([#1315](#1315)) by @spomichter
- **Python example restored** — Added back the Python usage example. ([#1317](#1317)) by @jeff-hykin
- **Nix install updated** — Replaced uv with pip for Nix compatibility. ([#1326](#1326)) by @ruthwikdasyam
- **README improvements** ([#1311](#1311)) by @paul-nechifor
- **Simplified writing docs** — Consolidated writing_docs to a single markdown file. ([#1254](#1254)) by @jeff-hykin

---

## 🏗️ CI & Build

- **ci-complete gate** — Dynamic branch protection via single aggregated status check. MD-only PRs no longer blocked. ([#1279](#1279)) by @spomichter
- **Path-based test filtering** — Test jobs fully skip (no container spin-up) when no relevant code changed. ([#1284](#1284), [#1286](#1286)) by @spomichter
- **Navigation docker build workflow** — CI builds for the ROS navigation stack. ([#1259](#1259)) by @spomichter
- **CUDA test marker** — `@pytest.mark.cuda` for GPU-dependent tests. ([#1220](#1220)) by @jeff-hykin
- **e2e test marker** — Marked end-to-end tests for selective CI runs. ([#1110](#1110)) by @paul-nechifor
- **pytest stdin fix** — Added `-s` to default addopts for LCM autoconf compatibility. ([#1320](#1320)) by @spomichter

---

## ⚠️ Breaking Changes

- **RPC renames**: `ModuleBlueprint` → `_BlueprintAtom`, `ModuleBlueprintSet` → `Blueprint`, `ModuleConnection` → `Stream` ([#1143](#1143))
- **Image class rewrite**: `CudaImage` and `NumpyImage` removed. Image is now a pure NumPy dataclass. Methods like `solve_pnp`, `csrt_tracker`, `from_depth`, `to_depth_meters` removed. ([#1161](#1161))
- **ROS messages removed from core**: All `to_ros`/`from_ros` conversion methods removed. Use `ROSTransport` instead. ([#1230](#1230))
- **QuestButtons → Buttons**: Renamed with generic field names. ([#1261](#1261))
- **RerunBridge replaces old rerun init**: `dimos.dashboard.rerun_init` removed. Use `RerunBridgeModule` or the `rerun-bridge` CLI. ([#1154](#1154))
- **Unitree directory restructuring**: `unitree_go2` → `unitree/go2`, `unitree_g1` → `unitree/g1`. Blueprint names updated. ([#1221](#1221))
- **Default viewer is now rerun-web**: Use `--viewer-backend rerun` to restore native viewer. ([#1324](#1324))

---

## Quickstart

```bash
# Install
uv pip install dimos[base,unitree]

# Try it (no hardware needed)
# NOTE: First run downloads ~2.4 GB from LFS
dimos --replay run unitree-go2

# Simulate
uv pip install dimos[base,unitree,sim]
dimos --simulation run unitree-go2
```

---

## New Contributors 🎉

- @ruthwikdasyam — Quest VR teleoperation, phone teleop, arm teleop, webcam rerun viz
- @JalajShuklaSS — GraspGen integration
- @jca0 — MuJoCo simulation module
- @christiefhyang — PR template

---

**Full Changelog**: [v0.0.9...v0.0.10](v0.0.9...v0.0.10)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants