Cookbook: Add A Simulator Adapter

Use an installed extension package when a benchmark has a live environment that can be stepped in closed loop. No OpenWAM source edit is required.

Implement The Contract

Implement SimulatorBackend from open_wam.sdk.simulator. The adapter owns environment construction and lifecycle, observation extraction, model-action conversion, stepping, success detection, rendering, and close.

Keep dependency-heavy simulator imports inside the factory so importing the extension hook remains lightweight.

from open_wam.sdk.simulator import (
    SimulatorFactoryContext,
    register_simulator_adapter,
)


def build_adapter(context: SimulatorFactoryContext):
    from acme_sim import Environment

    from .adapter import AcmeAdapter

    environment = Environment(
        asset_root=context.local_paths.get("simulators.acme_root"),
        endpoint=context.options.get("endpoint", "local"),
    )
    return AcmeAdapter(environment)


def register_open_wam() -> None:
    register_simulator_adapter("acme", build_adapter)

The backend should expose reset with EpisodeSpec, return SimulatorObservation, translate model actions, and return SimulatorStepResult from each step.

Run It

openwam-sim-rollout \
  --extension acme_open_wam \
  --benchmark acme \
  --sim-option endpoint=localhost:5000 \
  --cfg experiment.yaml \
  --checkpoint checkpoint_step_1000

Use --sim-option KEY=VALUE for application-owned construction values. Put machine paths in configs/local_paths.yaml; the factory receives the resolved mapping through context.local_paths.

Validation

Test the factory and backend against fake environments in ordinary CI. Add one closed-loop smoke that checks reset, action conversion, step, success, render, and close. Real simulator checks belong in self-hosted, scheduled, or label-gated jobs that retain result JSON and videos.

Only built-in integrations maintained by this repository belong under src/open_wam/integrations/. A third-party benchmark should remain an out-of-tree extension.