RUNBOOK
How to run Resilience EAS
EAS Resilience Evaluation is based on Habitat-Sim 3.0; its dataset and benchmark formats follow the PARTNR Planner. A separate LLM Judge acts as the critic, while stage baselines are calculated from EAS execution experience.
On this page
01. Environment installation
Refer to the installation instructions from both projects: PARTNR Planner Installation and Habitat-Sim Installation. The commands below retain the PARTNR-compatible pinned versions.
-
Create the environment
conda create -n habitat-llm python=3.9.2 cmake=3.14.0 -y conda activate habitat-llm -
Install the simulator stack
Match the CUDA build to the target machine. PARTNR currently pins PyTorch 2.4.1 and Habitat-Sim 0.3.3.
conda install pytorch==2.4.1 torchvision==0.19.1 \ torchaudio==2.4.1 pytorch-cuda=12.4 -c pytorch -c nvidia -y conda install habitat-sim=0.3.3 withbullet headless \ -c conda-forge -c aihabitat -y -
Install the project
Run this inside the full PARTNR-compatible Resilience EAS checkout containing
third_party/andrequirements.txt.git submodule sync git submodule update --init --recursive pip install -e ./third_party/habitat-lab/habitat-lab pip install -e ./third_party/habitat-lab/habitat-baselines pip install -e ./third_party/transformers-CFG pip install -r requirements.txt -e . -
Download and verify assets
Use the PARTNR guide to download HSSD, PARTNR episodes, OVMM objects, and Habitat rearrangement assets.
test -f data/datasets/partnr_episodes/v0_0/val_mini.json.gz test -f data/datasets/partnr_episodes/v0_0/val.json.gz test -d data/hssd-hab test -d data/objects_ovmm test -f evaluation/perturbation_banks/instruction_context_v6/manifest.jsonExpected: all five checks exit with status 0. The frozen stress bank is required only for Resilience Case Analysis.
-
Run the upstream smoke test
The official PARTNR Quickstart includes a heuristic baseline that does not require an LLM.
python -m habitat_llm.examples.planner_demo \ --config-name baselines/heuristic_full_obs.yaml \ habitat.dataset.data_path="data/datasets/partnr_episodes/v0_0/val_mini.json.gz"
02. Planner runtime configuration
-
Set planner variables
cd /path/to/resilience-eas export DATASET=data/datasets/partnr_episodes/v0_0/val_mini.json.gz export PARTNR_BENCHMARK=data/datasets/partnr_episodes/v0_0/val.json.gz export SERVED_MODEL_NAME="your-served-model-name" # Example (Qwen): SERVED_MODEL_NAME="Qwen3.5-9B-YOYO-Instruct" export QWEN35_SERVED_MODEL="$SERVED_MODEL_NAME" # config compatibility -
Check the Hydra config chain
test -f habitat_llm/conf/baselines/qwen35_centralized_zero_shot_react_summary_vllm.yaml test -f habitat_llm/conf/baselines/qwen35_centralized_zero_shot_react_summary_vllm_state.yaml test -f habitat_llm/conf/evaluation/resilience_config.yaml test -f habitat_llm/conf/experiments/resilience_joint_qwen35.yaml test -f habitat_llm/conf/instruct/qwen_few_shot_centralized_motoronly_new.yaml -
Start the vLLM server
Run the generic OpenAI-compatible server in a dedicated GPU terminal. See the vLLM execution notes.
conda activate vllm export MODEL_PATH="/path/to/your/model" export SERVED_MODEL_NAME="your-served-model-name" # Example (Qwen): MODEL_PATH="/path/to/Qwen3.5-9B-YOYO-Instruct" # Example (Qwen): SERVED_MODEL_NAME="Qwen3.5-9B-YOYO-Instruct" python -m vllm.entrypoints.openai.api_server \ --model "$MODEL_PATH" --served-model-name "$SERVED_MODEL_NAME" \ --host 127.0.0.1 --port 8000 --trust-remote-code -
Check the planner service
python -c "import hydra, habitat, habitat_sim, habitat_llm" curl -fsS http://127.0.0.1:8000/v1/completions \ -H 'Content-Type: application/json' \ --data "{\"model\":\"$SERVED_MODEL_NAME\",\"prompt\":\"ping\",\"max_tokens\":1}" >/dev/nullExpected: the Habitat imports succeed and the local planner completion request exits with status 0.
03. How to run EAS Case Analysis
Run one ordinary PARTNR episode. This validates the simulator, planner, task result, and trace path.
-
Run episode 78
EAS_RUN="outputs/habitat_llm/eas-78-$(date +%Y%m%d-%H%M%S)" python -m habitat_llm.examples.planner_demo_mp_new \ --config-name baselines/qwen35_centralized_zero_shot_react_summary_vllm \ hydra.run.dir="$EAS_RUN" habitat.dataset.data_path="$DATASET" \ '++episode_ids=[78]'Config:
baselines/qwen35_centralized_zero_shot_react_summary_vllm.yaml→evaluation/centralized_evaluation_runner_motortoolsonly_multiagent.yaml→instruct/qwen_few_shot_centralized_motoronly_new.yaml. -
Check the result
test -s "$EAS_RUN/results/episode_result_log.csv" test -s "$EAS_RUN/results/$(basename "$DATASET")/stats/78.json"Pass: both files exist. Task failure is still a valid measured outcome.
04. How to run Resilience EAS Case Analysis
Run the frozen 10 episodes × 3 seeds × 6 intensity levels (λ = 0, .2, .4, .6, .8, 1): 180 rollout cells. Complete the separate LLM Judge preflight first. See the Case Analysis execution notes.
-
Audit the frozen stress bank
python -m dataset_generation.benchmark_generation.audit_perturbation_bank \ --bank-path evaluation/perturbation_banks/instruction_context_v6 \ --dataset-path "$DATASET" \ --report-path evaluation/perturbation_bank_audit_v6.jsonPass:
status=complete, 30 valid entries, 180 resolved levels,formal_valid=true, and no errors. -
Run the 10 × 3 × 6 grid
RES_RUN="outputs/habitat_llm/resilience-case-$(date +%Y%m%d-%H%M%S)" python -u -m habitat_llm.examples.planner_demo_expq1 \ --config-name experiments/resilience_joint_qwen35 \ hydra.run.dir="$RES_RUN" habitat.dataset.data_path="$DATASET"Config:
experiments/resilience_joint_qwen35.yaml→baselines/qwen35_centralized_zero_shot_react_summary_vllm_state.yaml+evaluation/resilience_config.yaml+instruct/qwen_few_shot_centralized_motoronly_new.yaml. -
Check 180 rollout cells
CASE="$RES_RUN/results/resilience_joint_qwen35/joint_resilience_evaluation" python - "$CASE/raw_rollouts.csv" <<'PY' import csv, sys rows = list(csv.DictReader(open(sys.argv[1]))) cols = ("episode_id", "perturbation_seed", "perturbation_type", "perturbation_intensity") cells = {tuple(row[col] for col in cols) for row in rows} assert len(rows) == len(cells) == 180 print("180/180 unique cells") PY python -c 'import json,sys; assert json.load(open(sys.argv[1]))["subexperiments"][0]["row_count"]==180' \ "$RES_RUN/results/resilience_joint_qwen35/suite_manifest.json"Expected:
180/180 unique cellsand a 180-row suite manifest. -
Check lifecycle workers
python - "$CASE/conditions/default/runner/stress_sweep" <<'PY' import glob, json, sys paths = glob.glob(sys.argv[1] + "/**/execution_manifest.json", recursive=True) counts = [b["status_counts"] for path in paths for b in json.load(open(path))["batches"]] assert paths and all(x["failed"] == x["not_started"] == 0 for x in counts) print(f"{len(paths)} worker manifest(s) complete") PYExpected: no failed or unstarted workers. Calibration and critic-training workers may make the completed count greater than 180.
05. How to run Resilience EAS Benchmark
Run the project-defined first 400 entries of the installed PARTNR v0_0/val split. This is an upstream task benchmark, not the 10 × 3 × 6 resilience grid. The linked PARTNR execution notes provide the execution pattern.
-
Check the source split
python -c 'import gzip,json,sys; n=len(json.load(gzip.open(sys.argv[1],"rt"))["episodes"]); print(n); assert n>=400' \ "$PARTNR_BENCHMARK"Pass: the installed source split contains at least 400 entries.
-
Run the first 400 entries (indices 0–399)
BENCH_RUN="outputs/habitat_llm/benchmark-400-$(date +%Y%m%d-%H%M%S)" EPISODES="[$(seq -s, 0 399)]" python -m habitat_llm.examples.planner_demo_mp_new \ --config-name baselines/qwen35_centralized_zero_shot_react_summary_vllm \ hydra.run.dir="$BENCH_RUN" habitat.dataset.data_path="$PARTNR_BENCHMARK" \ num_proc=4 "++episode_indices=$EPISODES"Config: the same baseline, evaluation runner, and few-shot instruction chain used by EAS Case Analysis.
Default:
num_proc=4enables isolated episode workers; admission remains capped at one worker with a 48 GiB available-memory gate. Raise the cap only after a target-host memory smoke test. -
Check 400 episode results
python -c 'import csv,json,sys; r=list(csv.DictReader(open(sys.argv[1]))); m=json.load(open(sys.argv[2])); assert len(r)==400==len({x["episode_id"] for x in r}) and m["status"]=="completed"; print("400/400 completed")' \ "$BENCH_RUN/results/episode_result_log.csv" \ "$BENCH_RUN/results/mp_workers/execution_manifest.json"Expected:
400/400 completedfrom unique merged episode rows and a completed worker manifest.
06. References
- PARTNR Planner Quickstart
- PARTNR environment and asset installation
- Habitat-Sim installation and source builds
- Habitat-Lab
- PARTNR project page
- Resilience EAS Case Analysis execution notes
- PARTNR benchmark execution notes
- Generic vLLM server setup
- Qwen vLLM server and client example
- PARTNR full-method run examples
- Terminal-Bench runbook structure reference
SEPARATE SERVICE
LLM Judge
The planner and Judge are independent model services. The planner selects actions; the Judge never executes actions. During Resilience Case Analysis, the Judge supplies structured critic scores, while clean EAS execution evidence is aggregated into StageBaselines scoped by Judge and episode.
| Service | Purpose | API | Used by |
|---|---|---|---|
| Planner | Selects Habitat actions | 127.0.0.1:8000/v1/completions | All runs |
| LLM Judge | Scores critic transitions | $HABITAT_LLM_BASE_URL/chat/completions | Resilience only |
-
Set the Judge endpoint
The canonical joint config uses
evaluation.critic.llm_model=gpt-5.1with deterministic inference. Set the endpoint explicitly so the Judge cannot fall back to the planner vLLM URL.export HABITAT_LLM_BASE_URL="https://your-judge-host.example/v1" export OPENAI_API_KEY="your-judge-api-key"Required: an OpenAI-compatible
/v1/chat/completionsendpoint with JSON response mode. Keep the API key out of files and version control. -
Run the structured Judge preflight
This calls the same RewardShaper preflight used by the project and consumes one Judge request.
python - <<'PY' import json, os from habitat_llm.evaluation.core.critic_lifecycle import preflight_reward_shaper result = preflight_reward_shaper({ "use_llm_shaping": True, "reward_shaper_json_mode": True, "llm_model": "gpt-5.1", "llm_base_url": os.environ["HABITAT_LLM_BASE_URL"], }) print(json.dumps(result, indent=2)) assert result["valid"], result["reason"] PYPass:
valid=truewith numericgoal_progress_score,rationality_score, andefficiency_score. -
Validate Judge and StageBaseline lineage
CASE="$RES_RUN/results/resilience_joint_qwen35/joint_resilience_evaluation" python - "$CASE/raw_rollouts.csv" <<'PY' import csv, sys rows = list(csv.DictReader(open(sys.argv[1]))) truthy = lambda value: str(value).lower() in {"1", "true", "yes"} fields = ("reward_shaper_valid", "critic_lifecycle_valid", "stage_baseline_available", "stage_baseline_episode_match") assert {row["judge_model"] for row in rows} == {"gpt-5.1"} assert {row["stage_baseline_alignment_scope"] for row in rows} == {"judge_model_episode"} assert all(all(truthy(row[field]) for field in fields) for row in rows) print("Judge lineage valid") PYExpected: every formal row uses the same Judge and an episode-matched StageBaseline. A different Judge requires its own clean calibration evidence.
EAS Resilience