The molmo_spaces JSON evaluation pipeline follows a structured lifecycle from CLI/programmatic entry to result collection. The process is managed primarily through run_evaluation() in molmo_spaces/evaluation/eval_main.py and executed by the JsonEvalRunner class.
High-level Execution Flow
- Initialization:
run_evaluation() resolves the evaluation config class, loads benchmark episodes from JSON, and resolves the task_horizon (prioritizing CLI overrides over the benchmark's task_horizon_sec). - Configuration: An evaluation config is instantiated via
create_eval_config(), which enforces evaluation-mode flags (e.g., seed=42, no action noise, no datagen profiler). CLI overrides for camera_config, camera_names, and light intensity are then applied. - Runner Setup:
JsonEvalRunner is initialized. It loads episodes from the benchmark_dir, handles truncation (via max_episodes or episode_idx), and derives task_sampler_config.house_inds and samples_per_house. It inherits from ParallelRolloutRunner to set up worker processes. - Execution:
JsonEvalRunner.run() dispatches work items to workers. Each worker executes process_single_house(), which iterates through EpisodeSpec objects, samples tasks using JsonEvalTaskSampler, and runs rollouts at the specified policy_dt_ms. - Output: Trajectories (
trajectories.h5) and per-episode artifacts are written to the output_dir. Finally, collect_episode_results() generates an EvaluationResults object containing success_count, total_count, output_dir, episode_results, and exp_config.
CLI args / programmatic call
│
▼
run_evaluation() (molmo_spaces/evaluation/eval_main.py)
├─ resolve eval config class (registry name or "module:Class")
├─ load benchmark episodes from JSON
├─ resolve task_horizon (CLI override > benchmark task_horizon_sec)
├─ create_eval_config(): instantiate eval config, force eval-mode flags
│ (no action noise, no datagen profiler, seed=42, output_dir, ...)
├─ apply CLI overrides (camera_config, camera_names, light intensity)
├─ JsonEvalRunner.patch_config(): attach EvalRuntimeParams
└─ JsonEvalRunner.adjust_robot(): wire robot_eval_override (if any)
│
▼
JsonEvalRunner.__init__()
├─ load_all_episodes(benchmark_dir)
├─ truncate to max_episodes / single episode_idx
├─ derive task_sampler_config.house_inds + samples_per_house
└─ super().__init__() -> ParallelRolloutRunner sets up workers
│
▼
JsonEvalRunner.run() -> ParallelRolloutRunner.run()
└─ for each (house_id, batch) work item, dispatch to workers
│
▼
ParallelRolloutRunner.process_single_house() (per worker)
├─ load_episodes_for_house() # JsonEvalRunner override
├─ for each EpisodeSpec:
│ prepare_episode_config()
│ get_episode_task_sampler() -> JsonEvalTaskSampler(exp_config, ep)
│ sample_task_from_spec() -> task = sampler.sample_task(...)
│ run_single_rollout() -> step env at policy_dt_ms
│ should_close_episode_task_sampler() (True for JSON eval)
└─ write trajectories.h5 + per-episode artifacts under output_dir
│
▼
collect_episode_results() + (optional) wandb logging
│
▼
EvaluationResults (success_count, total_count, output_dir, episode_results, exp_config)