Skip to content

feat(evals): Habitat navigation benchmark: text-only arms, planner arm, video, parallel cases - #4216

Draft
spomichter wants to merge 16 commits into
feat/typesafe-agentfrom
feat/typesafe-nav-eval
Draft

spomichter wants to merge 16 commits into
feat/typesafe-agentfrom
feat/typesafe-nav-eval

Conversation

@spomichter

@spomichter spomichter commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Contribution path

Draft. Stacked on feat/typesafe-agent (the TypeSafe module this benchmark evaluates), so the diff is the benchmark only. The TypeSafe v2 rewrite is #4212; the eval needs only set_trace_dir, world_state and agent_idle from the module and rebases onto whichever lands. Spec: engineering/typesafe-nav-benchmark/spec.md in the agents repo.

Problem

Compare the TypeSafe reactive agent, dimOS's planner, and coding agents with and without dimOS on long-horizon go-to-object navigation in real Habitat scenes, on an even text-only footing, with navigation-quality metrics, per-case video, and parallel execution.

Solution

One launch serves every arm: habitat-nav mcp-server demo-objects nav-skills, plus the agent's own modules.

  • Habitat native publishes odom (PoseStamped) and a body-frame lidar scan, so type-safe-agent and raw-robot-bridge autoconnect by name. No relays, no remap blueprints.
  • nav-skills: go_to(x, y, wait_s), stop_navigation, finish over the planner's goal / goal_reached / stop_movement topics. Goals carry the robot's floor z: HM3D houses are multi-level and a z=0 goal snaps onto furniture.
  • TopicAgent (dimos/evals/agents/topic.py): evaluate whatever is already running. Publishes the instruction (text, or its (x, y) as a PointStamped) on one topic and returns when another goes truthy; trajectory from a module's set_trace_dir trace. The planner arm and the TypeSafe arm are two configurations of it.
  • Raw bridge: topics allowlist, world_state/json (the same builder TypeSafe reads) and finished/json; ROBOT.md lists only the served topics.
  • HabitatEnvironment: a navmesh tour from the scene file is driven before the task so the planner has a map (it plans only over observed surface, like dimsim_house explores first); task_start_ts; record_topics.
  • nav_metrics: reached, time to object (run duration when never reached), facing, bumps (held command without displacement), path length, straightness, turn reversals; written as nav_metrics.json beside the recording.
  • Sim: raw_topics, extra_env, --rerun-open none, opt-in rrd, video (Xvfb + dimos-viewer + ffmpeg, viewer window only).
  • dimos evals run --case --parallel --container --repeat: one dimos evals run per case, in a container when given; docker/eval/Dockerfile.
  • Suite dimos.evals.suites.habitat_nav with an example HM3D scene file (furniture-sized boxes, 4 cases, tour). The scene file layout is the detection3d_array_to_dict one from Ruthwik/feat/dimsim object annotations #4208 plus cases and tour.

Since the first push

  • HSSD: ten scenes from feat(habitat): HSSD scene object and wall ground truth as Detection3DArray #4211's ground truth, misc/habitat/nav_cases.py picks targets with an easy / medium / hard mix (clearance, geodesic distance, detour) and writes cases plus a navmesh mapping tour per scene; 80 cases, end point on the object's centre. HSSD ships no navmesh: dimos/simulation/habitat/navmesh.py computes and caches one.
  • Two MLS planner fixes the benchmark surfaced: goal snapping tries one step of z before the robot's height (a goal on an object snapped to its top), and local_map / region_bounds pair across the last four messages (latest-of-each never matched when the cloud lagged a tick, the planner starved).
  • TypeSafe agent: task brief in the state, distance_m to the object's edge, a finished question that stops and publishes finished. Stool case: 300 s timeout with 14 bumps -> finished in 10 s, score 1.0.
  • --video per case, dimos evals media captions and grids, every case instruction opens with a task brief.

Matrix on one HSSD case (stool, 3 m): planner 0.91, TypeSafe 1.00 (10 s), Pi no-dimOS Astra 1.00 / Fable 1.00 / GPT-5.6 1.00 / Opus 0.91, dimcode Fable 0.91 / Astra 0.72.

How to Test

# planner alone
dimos evals run dimos.evals.suites.habitat_nav --agent dimos.evals.agents.topic \
  --set send=goal --set send_type=point --set done=goal_reached --set done_type=Bool
# TypeSafe agent (TYPESAFE_API_KEY)
dimos evals run dimos.evals.suites.habitat_nav --agent dimos.evals.agents.topic \
  --set 'modules=["type-safe-agent"]' --set trace=TypeSafeAgent
# coding agent with dimOS / without dimOS
dimos evals run dimos.evals.suites.habitat_nav --agent dimos.evals.agents.dimcode --set model=gpt-6-astra
dimos evals run dimos.evals.suites.habitat_nav --agent dimos.evals.agents.pi --set no_dimos=true --set model=gpt-6-astra

Verified on the example scene, chair case (score 0.8 each): planner reached in 10.9 s; Pi + Astra without dimOS wrote a Zenoh observer and navigator and reached in 34 s; dimcode + Astra with dimOS reached in 16 s with one go_to. viewer.mp4 captured headless. 285 unit tests, ruff, mypy and pre-commit pass.

Not yet run: the TypeSafe arm (no key on the box) and the container path of --parallel (Docker registry unreachable on the box; job command unit-tested).

Checklist

  • I have read and approved the CLA.

…m, video, parallel cases

One launch (`habitat-nav mcp-server demo-objects nav-skills`) serves every arm of a go-to-object
benchmark on Habitat scenes: the planner alone, the TypeSafe reactive agent, coding agents with
dimOS (`go_to` / `stop_navigation` / `finish` tools) and without it (`world_state`, `cmd_vel`,
`finished` over the raw bridge).

- Habitat native publishes `odom` (PoseStamped) and a body-frame `lidar` scan, so TypeSafe and
  the raw bridge autoconnect without relays or remaps.
- `nav-skills`: point-goal tools over the planner's `goal` / `goal_reached` topics; goals carry the
  robot's floor z (multi-level scenes snap a z=0 goal onto furniture).
- `TopicAgent`: evaluate whatever is already running by sending the instruction on one topic and
  waiting on another; trajectory from a module's `set_trace_dir` trace.
- Raw bridge `topics` allowlist plus `world_state/json` (the TypeSafe builder) and `finished/json`;
  ROBOT.md lists only the served topics.
- `HabitatEnvironment`: navmesh `tour` driven before the task so the planner has a map,
  `task_start_ts`, `record_topics`, `latest_pose` from `odom`.
- `nav_metrics`: reached, time to object, facing, bumps, path length, straightness, turn reversals.
- `Sim`: `raw_topics`, `extra_env`, `--rerun-open none`, opt-in `rrd`, `video` capture on Xvfb.
- `--case`, `--parallel`, `--container`, `--repeat`: one `dimos evals run` per case, in a container
  when given; `docker/eval/Dockerfile`.
- Suite `habitat_nav` with an example HM3D scene file (boxes, cases, tour).
- `misc/habitat/nav_cases.py`: cases (spawn, end point beside each target, geodesic distance) and
  a navmesh mapping tour per scene, from a ground-truth file in the #4211 layout.
- `dimos/simulation/habitat/navmesh.py`: HSSD ships no navmesh; compute one once (Go2-ish agent,
  static objects included), cache under `target/habitat/navmesh/`, used by the native and the
  generator.
- Suite scene files may reference ground truth by path; scenes whose file is absent are skipped.
  Ten HSSD scenes, 5 to 6 cases each, plus the HM3D example.
- `demo-objects` excludes labels matching `^wall` by default.
…benchmark targets

- Goal snapping tries one step of z before the robot's height, so a goal at an object's
  centre at floor height lands on the floor beside it instead of on its top.
- Local map and region bounds pair by stamp across the last four of each; with the cloud
  landing a tick after its bounds, latest-of-each never matched and the planner starved.
- `nav_cases.py` picks isolated targets (0.6 m of clear floor from other furniture) with the
  end point on the object's centre; the grader then measures arrival to the object's box.
- Metrics are also written beside the trajectory in the run directory.
…e distance, finished question

- The state carries a `task` summary (TypeSafe has no system prompt; context goes in the state).
- `distance_m` is measured to the object's nearest box edge; the centre of a large object is
  never reachable, so `touching` never fired and the agent jittered against the target.
- A `finished` yes/no question; when it fires the agent publishes `finished`, stops and clears
  the goal. Stool case: 300 s timeout with 14 bumps -> finished in 15 s, 0 bumps, score 1.0.
…s with a task brief

- `dimos evals run --video` captures the viewer per case; the runner moves `viewer.mp4` and any
  `viewer.rrd` next to the trajectory.
- `dimos evals media <run>...`: captions each case video with arm, case, score and metrics;
  `--grid` tiles one case across arms.
- Every case instruction opens with the task brief; the TypeSafe agent reads the last line as
  its goal (its own brief lives in the state).
- `nav_cases.py` classifies targets easy / medium / hard from clearance, geodesic distance and
  detour, and picks a mix per scene: 80 cases over the ten HSSD scenes.
…ve; planner and zenoh fixes

- Habitat native publishes the navmesh as `scene_map` + `scene_bounds`; `habitat-nav-gt` feeds it to
  the MLS planner in place of the ray-traced local map, so the planner has the whole floor at t=0
  and no robot drives before the task clock. The tour is gone from the suite.
- MLS planner: pairs a cloud with the nearest bounds within 0.5 s (bounds queue of 32) and warns
  when a goal is set without map updates. Goal snapping tries one step of z first.
- dimos-module: `DIMOS_ZENOH_SHM=0` disables zenoh shared memory for the natives; with it on, the
  raycaster-to-planner link delivered nothing in most launches on this host.
- `TopicAgent`: `done_when_still` ends a run once the robot moved and then stood still (the
  follower never reports goal_reached for a goal inside an object); goals at the robot's floor z.
- Video: capture the viewer window when found within 90 s, keep the ffmpeg log; `--video` on the
  runner, `dimos evals media` captions and grids; suite case ids unique per label.
…ger scan, panels collapsed, pointer parked; recording path in case metrics
…finished flag, trace usage and edge distance re-applied on the typed agent
… per goal coordinates; eval lidar band and finished threshold; HM3D end points on the objects
@codecov

codecov Bot commented Sep 18, 2026

Copy link
Copy Markdown

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
5987 1 5986 103
View the full list of 1 ❄️ flaky test(s)
dimos.e2e_tests.test_voice_browser::test_hold_to_talk_ships_a_decodable_recording[firefox]

Flake rate in main: 100.00% (Passed 0 times, Failed 6 times)

Stack Traces | 22.8s run time
voice_bridge = ('http://127.0.0.1:49902/', [])
fake_mic_page = <Page url='http://127.0.0.1:49902/'>

    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:49902/'>
mic        = <Locator frame=<Frame name= url='http://127.0.0.1:49902/'> selector='internal:testid=[data-testid="chat-audio_in-mic"s]'>
url        = 'http://127.0.0.1:49902/'
voice_bridge = ('http://127.0.0.1:49902/', [])

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.

This branch was successfully deployed

1 active deployment
cachix f8dedf45 Deployed Sep 19, 2026 by spomichter via cachix-build #10612
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.

1 participant