Fan-io#2364
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Greptile SummaryThis PR introduces a fan-I/O abstraction layer for
Confidence Score: 5/5Safe to merge — the fan-I/O wiring is correct, the bundle-scatter contract is well-tested, and the migrated modules preserve existing runtime behavior. The core algorithmic changes (interpolated align, scatter, normalize_to_bundle) are all covered by contract tests that pin boundary conditions, exact-match short-circuits, compute-once guarantees, and the tolerance gate. The migration of Detection3DModule and MarkerModule eliminates the old Rx alignment path and replaces it with a simpler, more testable pipeline. Transport keys in the blueprints are correctly updated to (port_name, message_type). The one finding is a private-symbol import used only for a type annotation that is never evaluated at runtime. dimos/perception/detection/module3D.py imports the private Important Files Changed
Sequence DiagramsequenceDiagram
participant Port as In Port(s)
participant Ingest as ingest()
participant Store as NullStore / Stream
participant Pipeline as pipeline()
participant Norm as normalize_to_bundle()
participant Scatter as scatter_to_ports()
participant OutPorts as Out Port(s)
Note over Port,Store: start() wires every In port
Port->>Ingest: message arrives (push)
Ingest->>Store: "stream.append(msg, ts=msg.ts)"
Note over Store,Pipeline: primary.live() drives pipeline
Store->>Pipeline: Observation[T] (pull / live tail)
Pipeline->>Pipeline: align(self.streams.sibling, tolerance, interpolator?)
Pipeline->>Pipeline: transform / map_data → FusedDetections
Pipeline->>Pipeline: "map_data → Bundle{port_name: payload}"
Pipeline->>Norm: "Stream[Bundle | T]"
Note over Norm: 1-Out raw payload? wrap in Bundle
Norm->>Scatter: Stream[Bundle] (one subscribe)
Scatter->>OutPorts: bundle[port_a] → Out port_a.publish()
Scatter->>OutPorts: bundle[port_b] → Out port_b.publish()
Note over Scatter,OutPorts: missing/None keys → port idle this tick
Reviews (7): Last reviewed commit: "gh-reviews: resolve-2" | Re-trigger Greptile |
The nav_vlm inline import had no circular/heavy-dep reason; the sibling geometry msgs are already imported at the top.
Detection2DArray imported Detection3DArray._label_for_detection across modules; a member used from outside isn't private. Rename to label_for_detection and update both callers and the bbox.py reference.
Replace # type: ignore[no-untyped-def] on _FakePort.subscribe/emit, _RecordingOut.publish and the _wait_until pollers with real parameter and return annotations.
Fan-io
Problem
StreamModuleonly supports 1 input and 1 output.Solution
Inregulates all emission times. The pipeline must construct a Bundle which is used to publish messages to multiple outputs.Proof the platform works
memory2/test_module.pyfusion tests (literal N:M)MarkerModulemigration (clearest before/after)Detection3DModulemigration (Rx -> pipeline, compute-once multi-out)Out of scope
module-as-transform in arbitrary outer chains (will be new mem2-native-modules PR).
What this branch is (the core deliverable)
StreamModulememory2/module.pyingest()seam;self.streams.<port>; no 1:1 gatememory2/fanio.pyBundle,scatter_to_ports,normalize_to_bundlememory2/stream.pyStream.align, optionalinterpolator=memory2/interpolators.pylerp_pose,interp_odommemory2/test_module.pyTwoInputFusion,ChainedFusion, multi-out compute-once, interpolationWhy the design
Before fan-I/O:
After fan-I/O:
Summary
Fan-I/O is the wrapper layer: mem2 fusion inside deployed StreamModules. That works today - and an important step toward modules that are just mem2 transforms: the same pipeline() you deploy on a robot should run in any mem2 chain, on a recorded SqliteStore, faster than realtime.
What survives from fan-I/O into that follow-up will be the core - every input as a mem2 stream, .align() / interpolator=, Bundle scatter, ingest() - but not the legacy port/transport wrapper around it.