Skip to content

[vibed experiment] Add Rust language interop example#1622

Open
leshy wants to merge 8 commits intodevfrom
feat/rust-interop-example
Open

[vibed experiment] Add Rust language interop example#1622
leshy wants to merge 8 commits intodevfrom
feat/rust-interop-example

Conversation

@leshy
Copy link
Copy Markdown
Contributor

@leshy leshy commented Mar 20, 2026

Summary

  • Adds a Rust example to examples/language-interop/rust/ matching the existing C++, Lua, and TypeScript examples
  • Implements a minimal pure-Rust LCM UDP multicast transport (~100 lines, no system LCM library needed)
  • Subscribes to /odom (PoseStamped) and publishes /cmd_vel (Twist) at 10 Hz
  • Uses generated Rust message types from dimos-lcm#20 (rust-codegen branch)

Dependencies

  • lcm-msgs git dependency from dimos-lcm rust-codegen branch — once that PR merges, update to main
  • byteorder and socket2 from crates.io (no system deps)

Test plan

  • cargo check passes
  • cargo build --release passes
  • cargo clippy passes with no warnings
  • End-to-end test with simplerobot (requires running robot instance)

🤖 Generated with Claude Code

leshy and others added 2 commits March 21, 2026 00:48
Adds a Rust robot control example that subscribes to /odom (PoseStamped)
and publishes /cmd_vel (Twist) via LCM UDP multicast, matching the
existing C++/Lua/TypeScript interop examples.

Includes a minimal LCM transport implementation using socket2 and the
generated Rust message types from dimos-lcm.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
leshy and others added 2 commits March 21, 2026 01:16
Move LCM UDP transport to the dimos-lcm repo where it belongs (matching
the TS @dimos/lcm pattern). The example now depends on the dimos-lcm
crate for transport and lcm-msgs for message types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ep loop

- Pin dimos-lcm and lcm-msgs deps to specific rev instead of branch
  (branch refs are mutable; rev is immutable for reproducible builds)
- Replace fixed 1ms sleep with deadline-aware sleep capped at 10ms,
  avoiding unnecessary CPU wake-ups while still being responsive

Addresses review comments on PR #1622.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@leshy
Copy link
Copy Markdown
Contributor Author

leshy commented Mar 20, 2026

Non-interactive Rust LCM interop test results

Added automated tests to the rust-codegen branch of dimos-lcm (commit d188578):

7 roundtrip/binary tests (generated/rust_lcm_msgs/tests/roundtrip.rs):

  • Vector3, Twist, PoseStamped encode/decode roundtrips
  • Fingerprint consistency (same struct → same first 8 bytes)
  • Corruption detection (flipped fingerprint byte → Err)
  • Exact binary layout verification (known byte values for Vector3{1.5, 2.5, 3.5} and Twist fingerprint)

4 UDP multicast pubsub tests (tools/rust/lcm/tests/loopback.rs):

  • try_recv returns None on empty channel
  • Typed publish+receive for Vector3 and Twist (bounded retries, no manual steps)
  • Raw byte round-trip

All 11 tests pass. No external services, no interactive steps.

A few non-blocking observations for the PR author:

  1. transport.rs: stack-allocates 64 KB per try_recv call — could pre-allocate as a struct field
  2. transport.rs: publish heap-allocates a Vec per message — same fix applies
  3. encode_one/decode_one: consider pub(crate) visibility to prevent fingerprint bypass
  4. encode_one currently panics on I/O error — returning io::Result<()> would be more idiomatic
  5. encoded_size() excludes the 8-byte fingerprint header — worth a doc comment to clarify

None of these block the PR.

@dimensionalOS dimensionalOS deleted a comment from greptile-apps bot Mar 20, 2026
lesh and others added 4 commits March 21, 2026 02:42
Wire compat tests validate Python/Rust binary format agreement
(fingerprints, known byte layouts, roundtrips) without needing
external toolchains. Subprocess integration tests run Rust, TS,
and C++ binaries against headless simplerobot via LCM, skipping
gracefully when toolchains are unavailable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds test_lua_interop.py following the same subprocess/graceful-skip
pattern as Rust/TS/C++ tests. Also adds LUA_DIR constant to conftest.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@leshy
Copy link
Copy Markdown
Contributor Author

leshy commented Mar 20, 2026

Non-interactive language interop tests added

Following the request to add non-interactive tests for Rust and other languages, the following test files were added to examples/language-interop/tests/:

Wire compatibility tests (test_wire_compat.py) — pure Python, no build tools needed:

  • 6 tests verifying LCM message byte layout matches known values for Vector3, Twist, PoseStamped
  • Cross-language compat: Python-encoded bytes checked against Rust-expected values

Subprocess integration tests (skip gracefully if toolchain absent):

  • test_rust_interop.py — runs cargo build + Rust binary, checks pose subscription and twist publication
  • test_ts_interop.py — runs Deno TypeScript example
  • test_cpp_interop.py — runs cmake build + C++ example
  • test_lua_interop.py — runs Lua example

All integration tests use @pytest.mark.interop, skip if toolchain not found, and respect CI_NO_MULTICAST=1 env var.

Run wire-only tests with no dependencies: pytest examples/language-interop/tests/test_wire_compat.py --import-mode=importlib

os.environ.get("CI_NO_MULTICAST") is not None,
reason="multicast unavailable",
)
def test_rust_binary_receives_pose(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should be marked with slow

return str(e.stdout or ""), str(e.stderr or "")


def test_rust_binary_publishes_twist(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should be marked with slow.

pytestmark = pytest.mark.interop


def test_cpp_receives_pose_and_publishes_twist(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two of the tests fail for me:

_________________________________________________________________________________________ test_cpp_receives_pose_and_publishes_twist __________________________________________________________________________________________

simplerobot = <Popen: returncode: None args: ['/home/p/pro/dimensional/dimos/.venv/bin/pyt...>, cpp_binary = PosixPath('/home/p/pro/dimensional/dimos/examples/language-interop/cpp/build/robot_control')

    def test_cpp_receives_pose_and_publishes_twist(
        simplerobot: subprocess.Popen[str],
        cpp_binary: Path,
    ) -> None:
        """Run the C++ binary for a few seconds and verify message exchange."""
        try:
            result = subprocess.run(
                [str(cpp_binary)],
                capture_output=True,
                text=True,
                timeout=5,
            )
        except subprocess.TimeoutExpired as e:
            stdout = e.stdout or ""
            stderr = e.stderr or ""
        else:
            stdout = result.stdout
            stderr = result.stderr
    
>       assert "[pose]" in stdout, (
            f"C++ binary never received a PoseStamped.\nstdout: {stdout!r}\nstderr: {stderr!r}"
        )
E       TypeError: a bytes-like object is required, not 'str'

examples/language-interop/tests/test_cpp_interop.py:46: TypeError
__________________________________________________________________________________________ test_ts_receives_pose_and_publishes_twist __________________________________________________________________________________________

simplerobot = <Popen: returncode: None args: ['/home/p/pro/dimensional/dimos/.venv/bin/pyt...>, deno_available = None

    def test_ts_receives_pose_and_publishes_twist(
        simplerobot: subprocess.Popen[str],
        deno_available: None,
    ) -> None:
        """Run the Deno TS script for a few seconds and verify message exchange."""
        try:
            result = subprocess.run(
                ["deno", "run", "--allow-net", "--unstable-net", str(TS_DIR / "main.ts")],
                capture_output=True,
                text=True,
                timeout=5,
                cwd=str(TS_DIR),
            )
        except subprocess.TimeoutExpired as e:
            stdout = e.stdout or ""
            stderr = e.stderr or ""
        else:
            stdout = result.stdout
            stderr = result.stderr
    
>       assert "[pose]" in stdout, (
            f"TS script never received a PoseStamped.\nstdout: {stdout!r}\nstderr: {stderr!r}"
        )
E       TypeError: a bytes-like object is required, not 'str'

examples/language-interop/tests/test_ts_interop.py:55: TypeError
=================================================================================================== short test summary info ===================================================================================================
SKIPPED [1] examples/language-interop/tests/test_lua_interop.py:35: lua not found on PATH
FAILED examples/language-interop/tests/test_cpp_interop.py::test_cpp_receives_pose_and_publishes_twist - TypeError: a bytes-like object is required, not 'str'
FAILED examples/language-interop/tests/test_ts_interop.py::test_ts_receives_pose_and_publishes_twist - TypeError: a bytes-like object is required, not 'str'


from .conftest import LUA_DIR

pytestmark = pytest.mark.interop
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is the use for this interop mark?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please delete.

@paul-nechifor
Copy link
Copy Markdown
Contributor

@leshy These commits have Claude as a co-author... not sure if we care about that anymore.

Also, I assume Claude doesn't run pre-commit run --all-files before committing because I see CI commit fixes in there.

@@ -0,0 +1,51 @@
# Copyright 2026 Dimensional Inc.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Some of the scripts and docs use pytest dimos. That means these tests will not be executed. Can you please move them or change all those places to remove dimos.

Actually, the tests don't get executed at all even in CI because we have testpaths = ["dimos"] in pyproject.toml

@leshy
Copy link
Copy Markdown
Contributor Author

leshy commented Mar 21, 2026

yeah sorry, I should have tagged the PR (talked on discord on this also) was experimenting with fully autonomous PRs last night

@leshy leshy changed the title Add Rust language interop example [vibed experiment] Add Rust language interop example Mar 21, 2026
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