Skip to content

feat(simulation): opt-in ground-truth object pose stream for eval scoring - #3960

Open
Jerrybery wants to merge 1 commit into
dimensionalOS:mainfrom
Jerrybery:feat/sim-gt-object-poses
Open

feat(simulation): opt-in ground-truth object pose stream for eval scoring#3960
Jerrybery wants to merge 1 commit into
dimensionalOS:mainfrom
Jerrybery:feat/sim-gt-object-poses

Conversation

@Jerrybery

Copy link
Copy Markdown

Summary

Part 1 of 2 for #3594 (privileged ground-truth channel for interactive eval scoring).

MujocoSimModule gains an opt-in ground-truth stream:

  • publish_ground_truth: bool = False (default off — zero overhead, zero behavior change) and ground_truth_hz: float = 20.0 config fields
  • New gt_object_poses: Out[PoseStamped] stream: world poses of every free-joint scene body, throttled, frame_id = body name
  • Robot root is excluded (its pose already flows on odom); resolved by matching the same root qpos address odom uses
  • Leading / from MuJoCo 3.10's attached-submodel namespacing is stripped so frame_id stays a stable plain body name

Ground truth is privileged scoring data — never consumed by the agent. Part 2 will add the eval-side gt_store() accessor, a predicate library, and an xArm7 regression suite.

Test plan

  • 3 new tests in test_mujoco_sim_module.py: disabled-by-default publishes nothing; enabled publishes throttled poses; free-joint body resolution excludes the robot root against a real composed model
  • End-to-end smoke (not in repo): real blueprint run with a 2-object scene — 392 msgs / 10 s across both bodies with correct frame_ids, 0 msgs when disabled
  • Verified on Linux with the lockfile-pinned MuJoCo 3.10.0

Note for part 2 consumers: async def handle_<stream> auto-subscription uses a single-slot LATEST mailbox (module.py), so same-tick bursts overwrite; GT consumers must use direct subscribe() (as Recorder does).

…ring

MujocoSimModule gains publish_ground_truth (default off) and
ground_truth_hz (20 Hz). When enabled, world poses of every free-joint
scene body (robot root excluded — odom already covers it) publish on a
new gt_object_poses Out[PoseStamped] stream, frame_id = body name.

Ground truth is privileged scoring data for the evals framework
(issue dimensionalOS#3594): never consumed by the agent. Part 1 of 2; part 2 adds
the eval-side gt_store() and predicate library.
@github-actions github-actions Bot added the first-time-contributor PR opened by an author who had not previously committed to this repository label Sep 7, 2026
@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This change adds an opt-in, throttled ground-truth pose stream for eligible free-joint scene bodies. It is not safe to merge until ground_truth_hz rejects zero, negative, infinite, and NaN values: a zero rate crashes the simulator during post-step publishing, while other invalid values produce incorrect stream timing.

Confidence Score: 4/5

Not safe to merge because an enabled ground-truth stream can crash from an accepted configuration value.

Execution reproduced the failure in the ground-truth publishing path and also demonstrated incorrect behavior for other invalid rates.

Files Needing Attention: dimos/simulation/engines/mujoco_sim_module.py

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a proof for the posted P1 finding and attached two artifacts (a Python script and a run log) to support review.
  • T-Rex produced a second proof for another posted P1 finding with no artifacts attached.
  • T-Rex performed contract validation around the ground-truth throttle before executable, observed execution via a log, and noted the venv Python path issue while running the production method with Python 3.11.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
dimos/simulation/engines/mujoco_sim_module.py Adds throttled ground-truth pose publishing but accepts invalid publication rates that crash or mis-time the stream.

Comments Outside Diff (1)

  1. General comment

    P1 Enabled ground-truth stream crashes for a zero rate

    • Bug
      • With an enabled ground-truth body and ground_truth_hz=0.0, the post-step hook raises ZeroDivisionError: float division by zero before publishing. The same executable run shows -20.0 and inf publish immediately, while nan suppresses publication, so invalid rates have unbounded/inconsistent semantics.
    • Cause
      • MujocoSimModuleConfig.ground_truth_hz is an unconstrained float, and the throttle calculates 1.0 / self.config.ground_truth_hz without finite-positive validation.
    • Fix
      • Constrain ground_truth_hz at configuration validation time to a finite value strictly greater than zero.

    T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "feat(simulation): opt-in ground-truth ob..." | Re-trigger Greptile

# free-joint scene bodies (robot root excluded, odom covers it). Never
# consumed by the agent. Off = zero overhead, zero behavior change.
publish_ground_truth: bool = False
ground_truth_hz: float = 20.0

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.

P1 Validate ground-truth rate

When ground-truth publishing is enabled, ground_truth_hz=0 reaches the throttle calculation and raises ZeroDivisionError in the post-step callback. Negative, infinite, and NaN rates are also accepted, causing immediate or permanently suppressed publishing instead of the requested stream. Require a finite value strictly greater than zero during configuration validation.

Artifacts

Evidence from the check

  • This authored harness extracts and runs the unchanged production post-step method with enabled ground-truth publishing and invalid rates, showing the affected execution path.

Command output from the check

  • This command capture shows zero raises ZeroDivisionError, while negative and infinity publish and NaN suppresses publication, confirming invalid values are not safely handled.

View artifacts

T-Rex Ran code and verified through T-Rex

@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
5067 3 5064 44
View the top 3 failed test(s) by shortest run time
dimos.agents.mcp.test_mcp_client::test_image
Stack Traces | 12.2s run time
agent_setup = <function agent_setup.<locals>.fn at 0x7efab757e0c0>

    def test_image(agent_setup):
        history = agent_setup(
            blueprints=[Visualizer.blueprint()],
            messages=[
                HumanMessage(
                    "What do you see? Take a picture using your camera and describe it. "
                    "Please mention one of the words which best match the image: "
                    "'stadium', 'cafe', 'battleship'."
                )
            ],
            system_prompt="You are a helpful assistant that can use a camera to take pictures.",
        )
    
        response = history[-1].content.lower()
>       assert "cafe" in response
E       AssertionError: assert 'cafe' in 'you are currently on bourbon street.'

agent_setup = <function agent_setup.<locals>.fn at 0x7efab757e0c0>
history    = [HumanMessage(content="What do you see? Take a picture using your camera and describe it. Please mention one of the wo...s={}, response_metadata={}, id='lc_run--01a07a7a-939d-7473-aa6f-be94f50d93de-0', tool_calls=[], invalid_tool_calls=[])]
response   = 'you are currently on bourbon street.'

.../agents/mcp/test_mcp_client.py:197: AssertionError
dimos.agents.mcp.test_mcp_client::test_prompt
Stack Traces | 12.6s run time
+ Exception Group Traceback (most recent call last):
  |   File ".../dimos/dimos/.venv/lib/python3.12........./site-packages/_pytest/runner.py", line 341, in from_call
  |     result: TResult | None = func()
  |                              ^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12........./site-packages/_pytest/runner.py", line 242, in <lambda>
  |     lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise
  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/pluggy/_hooks.py", line 512, in __call__
  |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/pluggy/_manager.py", line 120, in _hookexec
  |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 167, in _multicall
  |     raise exception
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 139, in _multicall
  |     teardown.throw(exception)
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/_pytest/threadexception.py", line 92, in pytest_runtest_call
  |     yield from thread_exception_runtest_hook()
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/_pytest/threadexception.py", line 68, in thread_exception_runtest_hook
  |     yield
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 139, in _multicall
  |     teardown.throw(exception)
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/_pytest/unraisableexception.py", line 95, in pytest_runtest_call
  |     yield from unraisable_exception_runtest_hook()
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/_pytest/unraisableexception.py", line 70, in unraisable_exception_runtest_hook
  |     yield
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 139, in _multicall
  |     teardown.throw(exception)
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/_pytest/logging.py", line 846, in pytest_runtest_call
  |     yield from self._runtest_for(item, "call")
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/_pytest/logging.py", line 829, in _runtest_for
  |     yield
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 139, in _multicall
  |     teardown.throw(exception)
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
  |     return result.get_result()
  |            ^^^^^^^^^^^^^^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12............/site-packages/pluggy/_result.py", line 103, in get_result
  |     raise exc.with_traceback(tb)
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
  |     res = yield
  |           ^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 139, in _multicall
  |     teardown.throw(exception)
  |   File ".../dimos/dimos/.venv/lib/python3.12.../site-packages/_pytest/capture.py", line 898, in pytest_runtest_call
  |     return (yield)
  |             ^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 139, in _multicall
  |     teardown.throw(exception)
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
  |     return result.get_result()
  |            ^^^^^^^^^^^^^^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12............/site-packages/pluggy/_result.py", line 103, in get_result
  |     raise exc.with_traceback(tb)
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
  |     res = yield
  |           ^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 139, in _multicall
  |     teardown.throw(exception)
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
  |     return result.get_result()
  |            ^^^^^^^^^^^^^^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12............/site-packages/pluggy/_result.py", line 103, in get_result
  |     raise exc.with_traceback(tb)
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
  |     res = yield
  |           ^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 139, in _multicall
  |     teardown.throw(exception)
  |   File ".../dimos/dimos/.venv/lib/python3.12.../site-packages/_pytest/skipping.py", line 257, in pytest_runtest_call
  |     return (yield)
  |             ^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 121, in _multicall
  |     res = hook_impl.function(*args)
  |           ^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12........./site-packages/_pytest/runner.py", line 174, in pytest_runtest_call
  |     item.runtest()
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/_pytest/python.py", line 1627, in runtest
  |     self.ihook.pytest_pyfunc_call(pyfuncitem=self)
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/pluggy/_hooks.py", line 512, in __call__
  |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/pluggy/_manager.py", line 120, in _hookexec
  |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 167, in _multicall
  |     raise exception
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 139, in _multicall
  |     teardown.throw(exception)
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
  |     return result.get_result()
  |            ^^^^^^^^^^^^^^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12............/site-packages/pluggy/_result.py", line 103, in get_result
  |     raise exc.with_traceback(tb)
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
  |     res = yield
  |           ^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12.............................................................../site-packages/pluggy/_callers.py", line 121, in _multicall
  |     res = hook_impl.function(*args)
  |           ^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File ".../dimos/dimos/.venv/lib/python3.12....../site-packages/_pytest/python.py", line 159, in pytest_pyfunc_call
  |     result = testfunction(**testargs)
  |              ^^^^^^^^^^^^^^^^^^^^^^^^
  |   File ".../agents/mcp/test_mcp_client.py", line 167, in test_prompt
  |     history = agent_setup(
  |               ^^^^^^^^^^^^
  |   File ".../agents/mcp/conftest.py", line 87, in fn
  |     coordinator = ModuleCoordinator.build(blueprint)
  |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File ".../core/coordination/module_coordinator.py", line 378, in build
  |     coordinator.start_all_modules()
  |   File ".../core/coordination/module_coordinator.py", line 259, in start_all_modules
  |     safe_thread_map(modules, lambda m: m.start())
  |   File ".../dimos/utils/safe_thread_map.py", line 84, in safe_thread_map
  |     raise ExceptionGroup("safe_thread_map failed", errors)
  | ExceptionGroup: safe_thread_map failed (1 sub-exception)
  +-+---------------- 1 ----------------
    | dimos.protocol.rpc.rpc_utils.RemoteError: [Remote builtins.AttributeError] 'NoneType' object has no attribute 'subscribe'
    | 
    | Remote traceback:
    | Traceback (most recent call last):
    |   File ".../protocol/rpc/pubsubrpc.py", line 280, in execute_and_respond
    |     response = f(*args[0], **args[1])
    |                ^^^^^^^^^^^^^^^^^^^^^^
    |   File ".../protocol/rpc/spec.py", line 116, in override_f
    |     return getattr(module, fname)(*args, **kwargs)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File ".../agents/testing/agent_test_runner.py", line 51, in start
    |     self.register_disposable(Disposable(self.agent.subscribe(self._on_agent_message)))
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File ".../dimos/core/stream.py", line 303, in subscribe
    |     return self.transport.subscribe(cb, self)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
    | AttributeError: 'NoneType' object has no attribute 'subscribe'
    | 
    | 
    | The above exception was the direct cause of the following exception:
    | 
    | Traceback (most recent call last):
    |   File ".../dimos/utils/safe_thread_map.py", line 68, in safe_thread_map
    |     outcomes[idx] = fut.result()
    |                     ^^^^^^^^^^^^
    |   File ".../hostedtoolcache/Python/3.12.14........./x64/lib/python3.12....../concurrent/futures/_base.py", line 449, in result
    |     return self.__get_result()
    |            ^^^^^^^^^^^^^^^^^^^
    |   File ".../hostedtoolcache/Python/3.12.14........./x64/lib/python3.12....../concurrent/futures/_base.py", line 401, in __get_result
    |     raise self._exception
    |   File ".../hostedtoolcache/Python/3.12.14........./x64/lib/python3.12.../concurrent/futures/thread.py", line 59, in run
    |     result = self.fn(*self.args, **self.kwargs)
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File ".../core/coordination/module_coordinator.py", line 259, in <lambda>
    |     safe_thread_map(modules, lambda m: m.start())
    |                                        ^^^^^^^^^
    |   File ".../dimos/core/rpc_client.py", line 93, in __call__
    |     result, unsub_fn = self._rpc.call_sync(
    |                        ^^^^^^^^^^^^^^^^^^^^
    |   File ".../protocol/rpc/spec.py", line 88, in call_sync
    |     raise result
    | AttributeError: 'NoneType' object has no attribute 'subscribe'
    +------------------------------------
dimos.agents.skills.test_google_maps_skill_container::test_where_am_i
Stack Traces | 133s run time
agent_setup = <function agent_setup.<locals>.fn at 0x7fdc61a21120>

    def test_where_am_i(agent_setup) -> None:
>       history = agent_setup(
            blueprints=[FakeGPS.blueprint(), MockedWhereAmISkill.blueprint()],
            messages=[HumanMessage("What street am I on? Use the where_am_i tool.")],
        )

agent_setup = <function agent_setup.<locals>.fn at 0x7fdc61a21120>

.../agents/skills/test_google_maps_skill_container.py:74: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/agents/conftest.py:90: in fn
    coordinator = ModuleCoordinator.build(blueprint)
        agent_kwargs = {'mcp_server_url': 'http://localhost:21821/mcp', 'model_fixture': '.../agents/fixtures/test_where_am_i.json', 'system_prompt': None}
        agent_transport = <dimos.core.transport.pLCMTransport object at 0x7fdc621a4380>
        blueprint  = Blueprint(blueprints=(BlueprintAtom(kwargs={}, module=<class 'dimos.agents.skills.test_google_maps_skill_container.Fak...lobal_config_overrides=mappingproxy({}), remapping_map=mappingproxy({}), requirement_checks=(), configurator_checks=())
        blueprints = [Blueprint(blueprints=(BlueprintAtom(kwargs={}, module=<class 'dimos.agents.skills.test_google_maps_skill_container.Fa...obal_config_overrides=mappingproxy({}), remapping_map=mappingproxy({}), requirement_checks=(), configurator_checks=())]
        coordinator = None
        finished_event = <threading.Event at 0x7fdc621b5550: set>
        finished_transport = <dimos.core.transport.pLCMTransport object at 0x7fdc621a5f70>
        fixture    = None
        fixture_path = PosixPath('.../agents/fixtures/test_where_am_i.json')
        history    = [HumanMessage(content="What do you see? Take a picture using your camera and describe it. Please mention one of the wo... response_metadata={}, id='lc_run--01a07a7a-939d-7473-aa6f-be94f50d93de-0', tool_calls=[], invalid_tool_calls=[]), ...]
        lcm_url    = 'udpm://239.255.76.67:9521?ttl=0'
        mcp_url    = 'http://localhost:21821/mcp'
        messages   = [HumanMessage(content='What street am I on? Use the where_am_i tool.', additional_kwargs={}, response_metadata={})]
        on_message = <function agent_setup.<locals>.fn.<locals>.on_message at 0x7fdc61a2fec0>
        recording  = False
        request    = <SubRequest 'agent_setup' for <Function test_where_am_i>>
        system_prompt = None
        transports = [<dimos.core.transport.pLCMTransport object at 0x7fdc621a4380>, <dimos.core.transport.pLCMTransport object at 0x7fdc621a5f70>]
        unsubs     = [<function LCMPubSubBase.subscribe.<locals>.unsubscribe at 0x7fdc61a20540>, <function LCMPubSubBase.subscribe.<locals>.unsubscribe at 0x7fdc61a21300>]
.../core/coordination/module_coordinator.py:374: in build
    coordinator._connect_streams(blueprint, transports)
        blueprint  = Blueprint(blueprints=(BlueprintAtom(kwargs={}, module=<class 'dimos.agents.skills.test_google_maps_skill_container.Fak...lobal_config_overrides=mappingproxy({}), remapping_map=mappingproxy({}), requirement_checks=(), configurator_checks=())
        cls        = <class 'dimos.core.coordination.module_coordinator.ModuleCoordinator'>
        coordinator = <dimos.core.coordination.module_coordinator.ModuleCoordinator object at 0x7fdc621e0e60>
        global_values = {}
        module_kwargs = {}
        parsed_config = None
        transport_overrides = {}
        transports = {}
.../core/coordination/module_coordinator.py:332: in _connect_streams
    instance.set_transport(original_name, transport)  # type: ignore[union-attr]
        blueprint  = Blueprint(blueprints=(BlueprintAtom(kwargs={}, module=<class 'dimos.agents.skills.test_google_maps_skill_container.Fak...lobal_config_overrides=mappingproxy({}), remapping_map=mappingproxy({}), requirement_checks=(), configurator_checks=())
        bp         = BlueprintAtom(kwargs={'messages': [HumanMessage(content='What street am I on? Use the where_am_i tool.', additional_kw...=(ModuleRef(name='agent_spec', spec=<class 'dimos.agents.agent_spec.AgentSpec'>, optional=False),), instance_name=None)
        conn       = StreamRef(name='added', type=<class 'bool'>, direction='out')
        instance   = <dimos.core.rpc_client.RPCClient object at 0x7fdce5d61670>
        instance_key = 'agenttestrunner'
        key        = ('agent', <class 'langchain_core.messages.base.BaseMessage'>)
        original_name = 'agent'
        remapped_name = 'agent'
        self       = <dimos.core.coordination.module_coordinator.ModuleCoordinator object at 0x7fdc621e0e60>
        stream_type = <class 'langchain_core.messages.base.BaseMessage'>
        streams    = defaultdict(<class 'list'>, {('gps_location', <class 'dimos.mapping.models.LatLon'>): [('fakegps', 'gps_location'), ('...ished', <class 'bool'>): [('agenttestrunner', 'finished')], ('added', <class 'bool'>): [('agenttestrunner', 'added')]})
        transport  = <dimos.core.transport.pLCMTransport object at 0x7fdc621ac4d0>
        transports = {}
dimos/core/rpc_client.py:93: in __call__
    result, unsub_fn = self._rpc.call_sync(
        args       = ('agent', <dimos.core.transport.pLCMTransport object at 0x7fdc621ac4d0>)
        kwargs     = {}
        self       = <dimos.core.rpc_client.RpcCall object at 0x7fdce5d60200>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <dimos.protocol.rpc.pubsubrpc.LCMRPC object at 0x7fdce5d605c0>
name = 'AgentTestRunner/set_transport'
arguments = (('agent', <dimos.core.transport.pLCMTransport object at 0x7fdc621ac4d0>), {})
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")
E           TimeoutError: RPC call to 'AgentTestRunner/set_transport' timed out after 120.0 seconds

arguments  = (('agent', <dimos.core.transport.pLCMTransport object at 0x7fdc621ac4d0>), {})
event      = <threading.Event at 0x7fdce5d60800: unset>
method     = 'set_transport'
name       = 'AgentTestRunner/set_transport'
receive_value = <function RPCClient.call_sync.<locals>.receive_value at 0x7fdc61a225c0>
rpc_timeout = 120.0
self       = <dimos.protocol.rpc.pubsubrpc.LCMRPC object at 0x7fdce5d605c0>
unsub_fn   = <function PubSubRPCMixin.call_cb.<locals>.unsubscribe_callback at 0x7fdc61a228e0>

.../protocol/rpc/spec.py:83: TimeoutError

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

@Jerrybery

Copy link
Copy Markdown
Author

Part 2 of the #3594 split is implemented and pushed as a stacked branch on my fork: Jerrybery:feat/evals-gt-store (branched off this PR's head).

It adds the eval-side consumption of this PR's GT stream:

  • GTRecorder (Recorder subclass) records /gt_object_poses into a per-case db, kept separate from the agent-visible recording
  • EvalRunner.gt_store() + two-arg score(store, gt) scorers, plus a --mujoco-publish-ground-truth global flag so any mujoco blueprint emits GT during eval runs
  • dimos/evals/predicates.py spatial predicates (grasped() kept as a placeholder until the GT stream carries contact data)
  • suites/xarm7_tabletop.py regression suite

I'll open it as a PR once this one lands. Local verification: pytest dimos/evals/ 125 passed.

Note: the current tests (3.12) CI failure on this PR is a flaky agent-test issue unrelated to these changes (all three tests passed on the in-job --lf re-run, and the other Python-version jobs are green) — filed as #3962.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

first-time-contributor PR opened by an author who had not previously committed to this repository

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant