Skip to content

Fix RPC shell startup with older IPython versions - #4306

Merged
paul-nechifor merged 1 commit into
mainfrom
pim/fix/rpc-shell-ipython
Oct 5, 2026
Merged

paul-nechifor merged 1 commit into
mainfrom
pim/fix/rpc-shell-ipython

Conversation

@Nabla7

@Nabla7 Nabla7 commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator

What is this feature?

Remove --no-tip from the arguments passed to IPython when starting the RPC shell, and update the existing launch assertion.

Why do we need this?

The IPython version in the G1 environment rejects --no-tip, so dimos --transport zenoh shell exits before opening the prompt. Retaining --no-banner works across the tested versions. This compatibility fix is independent of the SONIC controller stack.

How to Test

uv run --no-sync pytest dimos/cli/test_shell.py

All nine shell tests and commit hooks passed. With a DimOS run available, open the shell with dimos --transport zenoh shell.

Which issue(s) does this PR close?

Extracted from #3557; no separate issue is closed.

Checklist

  • I have read and approved the CLA.

@github-actions github-actions Bot left a comment

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.

go2 replay realtime

Details
Benchmark suite Current: 549a5b2 Previous: edd7c34 Ratio
first frame wall 8.145 s 10.303 s 0.79
first frame cpu 16.742 s 21.873 s 0.77
run cpu 25.867 s 34.53 s 0.75
run cpu (user) 18.686 s 25.774 s 0.72
run cpu (system) 7.181 s 8.756 s 0.82
peak memory 2688.32 MB 2700.441 MB 1.00
peak threads 408 threads 394 threads 1.04
disk read 14.789 MB 25.445 MB 0.58
disk write 841.98 MB 788.336 MB 1.07

This comment was automatically generated by workflow using github-action-benchmark.

@greptile-apps

greptile-apps Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Low risk] Removes an IPython flag from shell startup.

Safe to merge; no actionable issues found.

What we checked:

  • Ran the RPC shell reproduction with a mocked coordinator and IPython startup across IPython 8.36.0 and 9.10.0, and observed that the nine focused shell tests passed. T-Rex
  • Compared original versus changed arguments: on IPython 8.36.0 the original failed with unrecognized --no-tip while the changed arguments reached the RPC prompt; on IPython 9.10.0 both versions reached the prompt. T-Rex
  • Observed the changed shell shows no startup tip, exposes the RPC namespace, and stops the mocked coordinator on exit. T-Rex
  • Validated the results against the uploaded Python repro script that runs both argument sets, confirming the same behavior across IPython versions as described above. T-Rex

Summary

The RPC shell now omits the unsupported IPython --no-tip argument while retaining --no-banner. The shell opened on IPython 8.36.0 and 9.10.0 without an extra startup tip, and all nine focused shell tests passed. No issues found.

Reviews (1) · Last reviewed commit: "fix(cli): omit unsupported IPython tip f..."

@codecov

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
6062 1 6061 297
View the top 3 failed test(s) by shortest run time
dimos.core.test_stream::test_subscription[ClassicSubscriber]
Stack Traces | 17.6s run time
dimos = <dimos.core.coordination.module_coordinator.ModuleCoordinator object at 0x72c61c4695b0>
subscriber_class = <class 'dimos.core.test_stream.ClassicSubscriber'>

    @pytest.mark.self_hosted
    @pytest.mark.skipif_macos_bug
    @pytest.mark.parametrize("subscriber_class", [ClassicSubscriber, RXPYSubscriber])
    def test_subscription(dimos, subscriber_class) -> None:
        robot = dimos.deploy(MockRobotClient)
    
        robot.lidar.transport = SpyLCMTransport("/lidar", PointCloud2)
        robot.odometry.transport = SpyLCMTransport("/odom", Odometry)
        robot.mov.transport = pLCMTransport("/mov")
    
        subscriber = dimos.deploy(subscriber_class)
    
        subscriber.odom.connect(robot.odometry)
    
        robot.start()
        subscriber.sub1()
        subscriber.wait_for_sub1_msg()
    
        assert subscriber.sub1_msgs_len() > 0
        assert subscriber.sub2_msgs_len() == 0
        assert subscriber.active_subscribers() == 1
    
        subscriber.sub2()
        subscriber.wait_for_sub2_msg()
    
        subscriber.unsub_all()
    
        assert subscriber.active_subscribers() == 0
        assert subscriber.sub1_msgs_len() != 0
>       assert subscriber.sub2_msgs_len() != 0
E       assert 0 != 0
E        +  where 0 = <dimos.core.rpc_client.RpcCall object at 0x72c61c469970>()
E        +    where <dimos.core.rpc_client.RpcCall object at 0x72c61c469970> = <dimos.core.rpc_client.RPCClient object at 0x72c61c468800>.sub2_msgs_len

dimos      = <dimos.core.coordination.module_coordinator.ModuleCoordinator object at 0x72c61c4695b0>
robot      = <dimos.core.rpc_client.RPCClient object at 0x72c616f635c0>
subscriber = <dimos.core.rpc_client.RPCClient object at 0x72c61c468800>
subscriber_class = <class 'dimos.core.test_stream.ClassicSubscriber'>

dimos/core/test_stream.py:200: AssertionError
dimos.core.test_stream::test_get_next
Stack Traces | 18.2s run time
dimos.protocol.rpc.rpc_utils.RemoteError: [Remote builtins.Exception] No value received after 10.0 seconds

Remote traceback:
Traceback (most recent call last):
  File ".../dimos/core/stream.py", line 55, in get_next
    .run()
     ^^^^^
  File ".../dimos/dimos/.venv/lib/python3.12.../reactivex/observable/observable.py", line 252, in run
    return run(self)
           ^^^^^^^^^
  File ".../dimos/dimos/.venv/lib/python3.12.../site-packages/reactivex/run.py", line 64, in run
    raise cast(Exception, exception)
Exception: Timeout

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File ".../protocol/rpc/zenohrpc.py", line 199, in _execute_rpc
    response = f(*args[0], **args[1])
               ^^^^^^^^^^^^^^^^^^^^^^
  File ".../protocol/rpc/spec.py", line 116, in override_f
    return getattr(module, fname)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../dimos/core/test_stream.py", line 126, in get_next
    return self.odom.get_next()
           ^^^^^^^^^^^^^^^^^^^^
  File ".../dimos/core/stream.py", line 58, in get_next
    raise Exception(f"No value received after {timeout} seconds") from e
Exception: No value received after 10.0 seconds


The above exception was the direct cause of the following exception:

dimos = <dimos.core.coordination.module_coordinator.ModuleCoordinator object at 0x72c61c46ba70>

    @pytest.mark.self_hosted
    @pytest.mark.skipif_macos_bug
    def test_get_next(dimos) -> None:
        robot = dimos.deploy(MockRobotClient)
    
        robot.lidar.transport = SpyLCMTransport("/lidar", PointCloud2)
        robot.odometry.transport = SpyLCMTransport("/odom", Odometry)
        robot.mov.transport = pLCMTransport("/mov")
    
        subscriber = dimos.deploy(RXPYSubscriber)
        subscriber.odom.connect(robot.odometry)
    
        robot.start()
        time.sleep(0.1)
    
        odom = subscriber.get_next()
    
        assert isinstance(odom, Odometry)
        assert subscriber.active_subscribers() == 0
    
        time.sleep(0.2)
    
>       next_odom = subscriber.get_next()

dimos      = <dimos.core.coordination.module_coordinator.ModuleCoordinator object at 0x72c61c46ba70>
odom       = Odom pos(→ Vector Vector([     0.4322     0.10804     0.31659])), rot(Quaternion(0.000772, -0.009128, 0.997689, 0.067324))
robot      = <dimos.core.rpc_client.RPCClient object at 0x72c61c4697c0>
subscriber = <dimos.core.rpc_client.RPCClient object at 0x72c61c46b950>

dimos/core/test_stream.py:236: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/core/rpc_client.py:93: in __call__
    result, unsub_fn = self._rpc.call_sync(
        args       = ()
        kwargs     = {}
        self       = <dimos.core.rpc_client.RpcCall object at 0x72c61c46b590>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <dimos.protocol.rpc.zenohrpc.ZenohRPC object at 0x72c61c46b4d0>
name = 'RXPYSubscriber/get_next', arguments = ((), {}), rpc_timeout = 120.0

    def call_sync(
        self, name: str, arguments: Args, rpc_timeout: float | None = None
    ) -> tuple[Any, Callable[[], None]]:
        if rpc_timeout is None:
            method = name.rsplit("/", 1)[-1]
            rpc_timeout = self.rpc_timeouts.get(name) or self.rpc_timeouts.get(
                method, self.default_rpc_timeout
            )
        event = threading.Event()
    
        def receive_value(val) -> None:  # type: ignore[no-untyped-def]
            event.result = val  # type: ignore[attr-defined]  # attach to event
            event.set()
    
        unsub_fn = self.call(name, arguments, receive_value)
        if not event.wait(rpc_timeout):
            # Retries register new callbacks. Remove this expired callback so
            # repeated timeouts do not accumulate entries in the shared response map.
            unsub_fn()
            raise TimeoutError(f"RPC call to '{name}' timed out after {rpc_timeout} seconds")
    
        # Check if the result is an exception and raise it
        result = event.result  # type: ignore[attr-defined]
        if isinstance(result, BaseException):
>           raise result
E           Exception: No value received after 10.0 seconds

arguments  = ((), {})
event      = <threading.Event at 0x72c61c46bf20: set>
method     = 'get_next'
name       = 'RXPYSubscriber/get_next'
receive_value = <function RPCClient.call_sync.<locals>.receive_value at 0x72c61c3e2020>
result     = Exception('No value received after 10.0 seconds')
rpc_timeout = 120.0
self       = <dimos.protocol.rpc.zenohrpc.ZenohRPC object at 0x72c61c46b4d0>
unsub_fn   = <function ZenohRPC.call_cb.<locals>.unsubscribe_callback at 0x72c61c3e2520>

.../protocol/rpc/spec.py:88: Exception
dimos.e2e_tests.test_voice_browser::test_hold_to_talk_ships_a_decodable_recording[firefox]
Stack Traces | 23.1s run time
voice_bridge = ('http://127.0.0.1:58582/', [])
fake_mic_page = <Page url='http://127.0.0.1:58582/'>

    def test_hold_to_talk_ships_a_decodable_recording(
        voice_bridge: tuple[str, list[AudioChunk]], fake_mic_page: Page
    ) -> None:
        url, chunks = voice_bridge
        chunks.clear()  # the module fixture is shared across both engines
        fake_mic_page.goto(url)
        mic = fake_mic_page.get_by_test_id("chat-audio_in-mic")
        # Enabled == transport connected; the manifest already placed the panel.
        expect(mic).to_be_enabled(timeout=120_000)
        expect(mic).to_have_attribute("data-state", "idle")
    
        mic.hover()
        fake_mic_page.mouse.down()
>       expect(mic).to_have_attribute("data-state", "recording", timeout=15_000)
E       AssertionError: Locator expected to have attribute 'recording'
E       Actual value: arming 
E       Call log:
E         - Expect "to_have_attribute" with timeout 15000ms
E         - waiting for get_by_test_id("chat-audio_in-mic")
E       
E       Aria snapshot:
E       - button "hold to talk": talk

chunks     = []
fake_mic_page = <Page url='http://127.0.0.1:58582/'>
mic        = <Locator frame=<Frame name= url='http://127.0.0.1:58582/'> selector='internal:testid=[data-testid="chat-audio_in-mic"s]'>
url        = 'http://127.0.0.1:58582/'
voice_bridge = ('http://127.0.0.1:58582/', [])

dimos/e2e_tests/test_voice_browser.py:104: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@paul-nechifor paul-nechifor added the backport:skip Skip creating a backport to any release branches label Oct 5, 2026
@paul-nechifor
paul-nechifor enabled auto-merge (squash) October 5, 2026 23:06
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Oct 5, 2026
@paul-nechifor
paul-nechifor merged commit 7554a44 into main Oct 5, 2026
60 of 64 checks passed
@paul-nechifor
paul-nechifor deleted the pim/fix/rpc-shell-ipython branch October 5, 2026 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip Skip creating a backport to any release branches ready-to-merge Required CI checks have passed on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants