rlmesh.adapters

Generalized env-to-model adapters.

import rlmesh.adapters

The guided tour is Adapters; the full role registry, every leaf option, and the conversion policy are in the Adapter Reference.

rlmesh.adapters derives the preprocessing and postprocessing between an environment and a model from declarative descriptions, instead of a hand-written adapter per pair.

The split is asymmetric. An environment tags its observation and action spaces: it names the semantic role of each entry plus the few facts the spaces cannot carry (image layout, rotation encoding, an explicit value range). A model fully specifies the payload it ingests and the action it emits. resolve() matches the two by role and produces an Adapter; widths, dtypes, and keys come from the gymnasium spaces.

Install the NumPy backend for direct adapter calls and the examples below:

pip install "rlmesh[numpy]"

Environment Tags

An environment publishes EnvTags in its contract metadata (via tag() or EnvServer(env, tags=...)), so a client can resolve an adapter from the handshake alone.

EnvTags.observation_roles (and Session.observation_roles on a live session) groups the declared roles by kind.

Action Layout

The action layout is a shared vocabulary. An environment tags the action vector its step accepts; a model declares the action vector it emits. The resolver converts between them per actuator.

Escape Hatches

When a pairing needs logic a declarative spec cannot express, three mechanisms compose, most local first. A custom input computes one payload slot from the raw observation while the rest stays spec-driven. A custom encoding handles a rotation convention the native crate does not ship. A custom adapter subclasses AdapterBase to add stateful behavior, typically by wrapping a resolved adapter and overriding only the stateful part.

Custom inputs

A Custom input runs host-language code that maps the raw observation to one payload slot. Custom(transform=fn) runs an in-process callable and is local only; Custom(entrypoint="module:callable") names a string that is imported only when you pass resolve(..., trust_entrypoints=True), so it can travel in a contract. A custom input receives the environment’s own keys, not roles, and returns the entire payload slot, so it does no role-matching, dim/index, or range-mapping, and is observation-side only.

Custom encodings

Rotation encodings are a closed vocabulary (the RotationEncoding set listed under Vocabulary). You cannot register one from Python, because a spec is data that travels in a contract and resolves on a remote client with no code. For a convention that is general and stable, like a published model’s rot6d_rowmajor, add it first-party: a few lines on the native RotationEncoding enum plus the Python Literal. It then works on both the observation and action sides, serializes into the contract, and is conformance-tested once.

For a bespoke or proprietary convention, declare a CustomEncoding on the nearest native base encoding (rot6d or a quaternion) and supply the host-side repacking. resolve lowers the field to its base for the native core, so role-matching, range-mapping, and the env-to-base conversion are unchanged; the adapter applies your transforms at the boundary: from_base after the native conversion on the observation side, to_base before it on the action side. Define the encoding once and reference it from both arms:

ROT6D_MINE = adapt.CustomEncoding(
    base="rot6d", from_base=rot6d_to_mine, to_base=mine_to_rot6d, name="rot6d_mine"
)

The packing must preserve the base width, so the part it tags keeps it: a dim restates that width or is omitted, and the part may sit at any offset of a multi-part Concat (the repack reads and writes exactly its own slice, whose offset the resolved plan reports). At resolve time the two arms are round-tripped on a probe to catch a mispaired encode/decode; pass resolve(..., check_inverse=False) to skip. The transforms are in-process callables, so the spec is local; a serializable module:callable form is planned.

When the constraints do not fit (a width-changing repack, or non-rotation feature engineering), drop to a custom AdapterBase or replace a whole payload slot with a Custom input. None of these attach a custom encoding to a role in the spec itself: the vocabulary stays closed so specs remain pure data that resolve on a remote client with no code. Reach for the boundary wrapper for a one-off; upstream the encoding once you want it attached to a role and reused.

Custom adapters

Subclass AdapterBase for stateful behavior a spec cannot describe (for example temporal ensembling across action chunks, or a width-changing rotation repack interior to a multi-field state). The usual shape wraps a resolved adapter and overrides only the stateful part. Override reset() to clear episode state and wire it to the model’s on_episode_end.

A pair override replaces the adapter for one specific (model, environment) pairing entirely, for cases like control-space conversion against a robot’s kinematic model. There is no special machinery: keep a registry keyed by the pair and consult it before resolving.

Vocabulary

Semantic roles are an open vocabulary of wire strings matched verbatim between independently authored tags and specs. The well-known conventions that ship with RLMesh are re-exported from the package (single-sourced from the native crate): the core roles IMAGE_PRIMARY, IMAGE_SECONDARY, IMAGE_WRIST, INSTRUCTION, JOINT_POS, JOINT_VEL, ACTION_JOINT_POS, ACTION_JOINT_VEL; the manipulation roles EEF_POS, EEF_ROT, GRIPPER_POS, EEF_WRENCH, ACTION_DELTA_POS, ACTION_DELTA_ROT, ACTION_GRIPPER, ACTION_EEF_POS, ACTION_EEF_ROT; and the body roles BASE_ANG_VEL, BASE_ROT, COMMAND_BASE_VEL. A role that repeats on a body (a second arm, a second wrist camera) is the same role under a part=: any identifier both sides agree on, with LEFT_ARM, RIGHT_ARM, HEAD, TORSO, BASE, LEFT_LEG, and RIGHT_LEG (PARTS) as the suggested spellings. The full registry, with wire strings and widths, is in Adapter Reference.

Rotation widths follow the declared encoding. rlmesh.adapters.ROTATION_DIMS maps each encoding to its dimension count:

Encoding Dims Convention
quat_xyzw 4 quaternion, scalar-last
quat_wxyz 4 quaternion, scalar-first
axis_angle 3 rotation vector
rot6d 6 first two columns of the rotation matrix, concatenated
rot6d_rowmajor 6 same two columns flattened row-major
euler_xyz 3 roll-pitch-yaw, extrinsic XYZ
gravity_xyz 3 projected gravity, an observation-side sink for BASE_ROT

rot6d is the standard 6D rotation; rot6d_rowmajor exists for checkpoints trained on the row-major interleaving. See Adapters for when to add an encoding versus reach for a custom encoding.

Classes

Functions

Exceptions

Submodules

Constants

Show all 32

Type aliases

Show all 16