Skip to main content
A saved policy is a directory, not a pickle. Every artifact bundles weights, architecture, robot config, and normalizer state so it can be loaded with zero context on another machine.

Layout

runs/go2-walk-v1/
├── rfx_config.json     # architecture + robot + training metadata
├── model.safetensors   # weights
└── normalizer.json     # observation normalizer state

Save

policy.save(
    "runs/go2-walk-v1",
    robot_config=config,
    normalizer=normalizer,
    training_info={"total_steps": 50000},
)

Load

loaded = rfx.load_policy("runs/go2-walk-v1")
loaded = rfx.load_policy("hf://rfx-community/go2-walk-v1")

loaded.policy         # reconstructed policy
loaded.robot_config   # RobotConfig or None
loaded.normalizer     # normalizer or None
loaded.policy_type    # "MLP", "ActorCritic", ...
LoadedPolicy is callable and handles torch / tinygrad conversion automatically.

Push and pull via HuggingFace Hub

rfx.push_policy("runs/go2-walk-v1", "rfx-community/go2-walk-v1")
loaded = rfx.load_policy("hf://rfx-community/go2-walk-v1")
Policies travel the same way datasets do.

Inspect without loading

config = rfx.inspect_policy("runs/go2-walk-v1")
print(config["policy_type"])    # "MLP"
print(config["policy_config"])  # {"obs_dim": 48, ...}
rfx deploy calls load_policy internally. If the artifact’s rfx_config.json records a robot, --robot is inferred automatically.