Skip to content

Layer 4 — rSkill (S1)

Part of the OpenRAL public-symbol inventory. Hand-curated; (LNN) markers are refreshed by tools/refresh_methods_linenos.py.

python/rskill/src/openral_rskill/base.py

rSkillBase — abstract base class with lifecycle state machine.

  • const _TRANSITIONS: dict[RSkillState, frozenset[RSkillState]] = {...} — Legal lifecycle edges keyed by current state; _require_transition checks membership. (L59)
  • class rSkillBase(abc.ABC) — Abstract base class for all OpenRAL skills (rSkill is the official package-format name). (L68)
  • __init__(name, *, version='0.1.0', role='s1', embodiment_tags=None, latency_budget_ms=None) — Init only; does not configure or load weights. (L98)
  • info -> RSkillInfo [@property] (L122)
  • name -> str [@property] (L130)
  • state -> RSkillState [@property] (L135)
  • configure() -> None — unconfigured → inactive. (L142)
  • activate() -> None — inactive → active. (L167)
  • deactivate() -> None — active → inactive. (L189)
  • shutdown() -> None — Any state → finalized. (L209)
  • step(world_state) -> Action — One inference step (hot path). (L233)
  • on_load_weights() -> None — Hook: load weights. (L279)
  • on_unload_weights() -> None — Hook: release weights, called by shutdown() (VRAM eviction). (L286)
  • on_quantize() -> None — Hook: apply quantization. (L296)
  • on_warmup() -> None — Hook: dummy forward pass, called by activate() before _activate_impl; default no-op. The deploy path implements it in the runner's policy shim (rskill_runner_node.py on_warmup), which runs adapter.step on a synthetic observation and swallows+logs failures as rskill_runner.warmup_failed. (L303)
  • _configure_impl/_activate_impl/_deactivate_impl/_shutdown_impl/_step_impl() [@abstractmethod] (L313)
  • private: _transition, _update, _require_transition, _enter_error

python/rskill/src/openral_rskill/runtime.py

Runtime Protocol and NullRuntime — inference backend contract.

  • class Runtime(Protocol) — Structural protocol for skill inference backends. (L24)
  • is_loaded -> bool [@property] (L40)
  • device -> str [@property] (L45)
  • load(path) -> None (L49)
  • infer(inputs) -> dict[str, Any] (L61)
  • quantize(config: QuantizationConfig) -> None (L76)
  • warmup(inputs) -> None (L88)
  • unload() -> None (L96)
  • class NullRuntime — No-op backend for testing; same surface as Runtime, every method a no-op. (L101)
  • __init__(device='cpu') (L123)
  • is_loaded -> bool [@property] (L133)
  • device -> str [@property] (L138)
  • load(path) -> None (L142)
  • infer(inputs) -> dict[str, Any] (L150)
  • quantize(config) -> None (L161)
  • warmup(inputs) -> None (L168)
  • unload() -> None (L175)

python/rskill/src/openral_rskill/runtime_pytorch.py

  • const _ALLOW_UNSAFE_PICKLE_ENV = "OPENRAL_ALLOW_UNSAFE_PICKLE" — Env var gating load()'s pickle deserialization (C2). (L38)
  • class PyTorchRuntime — torch-backed Runtime. (L41)
  • __init__(device='cpu') (L70)
  • is_loaded -> bool [@property] (L81)
  • device -> str [@property] (L86)
  • load(path) -> None — Unpickles a full module — gated behind _ALLOW_UNSAFE_PICKLE_ENV, C2. (L90)
  • load_safetensors(path, *, model, strict=True) -> None — Safe: loads a state_dict into a caller-supplied module, no code execution — preferred for new skills. (L146)
  • infer(inputs) -> dict[str, Any] (L202)
  • quantize(config) -> None — Dynamic INT8 on Linear. (L229)
  • warmup(inputs) -> None (L261)
  • unload() -> None — Frees CUDA cache. (L273)

python/rskill/src/openral_rskill/runtime_onnx.py

  • const _CUDA_PROVIDERS = ["CUDAExecutionProvider", "CPUExecutionProvider"] (L30)
  • const _CPU_PROVIDERS = ["CPUExecutionProvider"] (L31)
  • class ONNXRuntime — onnxruntime-backed Runtime. (L52)
  • __init__(device='cpu') (L80)
  • is_loaded -> bool [@property] (L100)
  • device -> str [@property] (L105)
  • load(path) -> None (L109)
  • infer(inputs) -> dict[str, Any] (L132)
  • quantize(config) -> None — Always raises — ONNX quantization is pre-applied. (L157)
  • warmup(inputs) -> None (L173)
  • unload() -> None (L186)

python/rskill/src/openral_rskill/backend_registry.py

Extraction seam: entry-point-based runtime-backend + policy-attach-hook registry.

  • const _RUNTIME_BACKENDS_GROUP = "openral.runtime_backends" (L41)
  • const _POLICY_ATTACH_HOOKS_GROUP = "openral.policy_attach_hooks" (L42)
  • const _BUILTIN_RUNTIME_BACKENDS: dict[str, str] = {...} — Built-in kind → runtime-class-name map (pytorch/onnx/null). (L48)
  • resolve_runtime_backend(kind: str) -> type[Runtime] — built-in dict (pytorch→PyTorchRuntime, onnx→ONNXRuntime, null→NullRuntime) first, else looked up via the openral.runtime_backends entry-point group (e.g. openral-pro-trt registering tensorrt). Miss → ROSConfigError naming openral-pro-trt. (L55)
  • maybe_attach_pro_hooks(policy_name: str, skill, **kwargs) -> bool — Generic policy-attach-hook lookup via the openral.policy_attach_hooks entry-point group (name = policy_name). No hook installed → debug log + False, never a silent skip; hook found → invoked as hook(skill, **kwargs). (L106)

Moved to OpenRAL Pro: runtime_tensorrt.py (TensorRTRuntime), smolvla_export.py, smolvla_trt.py, and act_trt.py now live in the private openral-pro-trt package as openral_pro_trt.*, plugging in via the openral.runtime_backends / openral.policy_attach_hooks entry-point groups above.

python/rskill/src/openral_rskill/loader.py

rSkill loader — HF Hub download, manifest validation, license guard, local registry.

  • const _DATA_HOME = Path(os.environ.get("OPENRAL_DATA_HOME", ...)) — Base dir for the local rSkill registry. (L66)
  • const _CACHE_HOME = Path(...) — Base dir HF Hub downloads land under. (L67)
  • const DEFAULT_REGISTRY_PATH: Path = _DATA_HOME / "rskills.json" — Default JSON registry file written by rSkill.from_pretrained. (L71)
  • const _RSKILL_MANIFEST_CACHE: dict[str, RSkillManifest] = {} — In-process memoisation for load_rskill_manifest. (L76)
  • const _ALLOW_NONCOMMERCIAL_ENV = "OPENRAL_ALLOW_NONCOMMERCIAL" — Env var gating noncommercial-weight loads (CLAUDE.md §1.9). (L80)
  • const _REQUIRE_SIGNED_ENV = "OPENRAL_REQUIRE_SIGNED_SKILLS" — Env var that fails closed when provenance signing is unverified. (L85)
  • const _EMBODIMENT_ANY_TAG: str = "any" — Sentinel embodiment tag matching every robot. (L93)
  • class InstalledRSkillEntry(BaseModel) — One row in the local registry. (L99) fields: repo_id, version, revision, local_dir, manifest_path, license, role, embodiment_tags, installed_at
  • class rSkill — Packaged, capability-tagged robot skill; provenance signing is not yet enforced. (L143)
  • __init__(manifest, local_dir) (L167)
  • from_pretrained(cls, repo_id, *, revision=None, cache_dir=None, force_download=False, commercial_use=True, registry_path=None) -> rSkill [@classmethod] — Download from HF Hub, validate, register. (L180)
  • from_yaml(cls, path, *, local_dir=None) -> rSkill [@classmethod] — Load locally without network. (L299)
  • list_installed(registry_path=None) -> list[InstalledRSkillEntry] [@staticmethod] (L334)
  • uninstall(repo_id, registry_path=None) -> bool [@staticmethod] — Remove from registry only. (L365)
  • check_embodiment_tags(manifest, robot_capabilities) -> None [@staticmethod] — Verify embodiment tag intersection (raises on disjoint sets). Exempt for perception kinds (detector/segmenter/vlm, _EMBODIMENT_AGNOSTIC_KINDS): they are camera-in → detections/text-out with no action contract, so they match any robot regardless of tags. (L395)
  • check_capability_flags(manifest, robot_capabilities) -> None [@staticmethod] — Verify every manifest.capabilities_required flag against RobotCapabilities. (L423)
  • check_runtime(manifest, robot_capabilities) -> None [@staticmethod] — Verify manifest.runtime ∈ gpu_supported_runtimes; skipped when the legacy capability field is missing or empty (the GPU support fields moved to ComputeSpec, so missing means "unknown" for now). (L455)
  • check_quantization_dtype(manifest, robot_capabilities) -> None [@staticmethod] — Verify manifest.quantization.dtype ∈ gpu_supported_dtypes; skipped when the legacy capability field is missing or empty. (L484)
  • check_capabilities(manifest, robot_capabilities) -> None [@staticmethod] — Composition of the four narrower checks; raises on first failure. (L512)
  • check_sensors(manifest, robot_sensors) -> None [@staticmethod] — Verify the robot exposes every SensorRequirement the manifest declares: a vla_feature_key requirement resolves to exactly one matching robot sensor, otherwise the robot must expose count sensors of the requested modality meeting any resolution minimum. ROSCapabilityMismatch on any unmet requirement. (L550)
  • check_compatibility(manifest, robot, *, scene_gripper_convention=None) -> None [@staticmethod] — Umbrella entry point combining check_capabilities (embodiment tags + capability flags, with compute=robot.compute_edge or robot.compute_local), check_sensors against robot.sensors, and check_gripper_conventions. (L591)
  • check_gripper_conventions(manifest, robot, *, scene_gripper_convention=None) -> None [@staticmethod] — Refuse (ROSCapabilityMismatch) a skill whose GRIPPER_* encoding (its gripper actuator's gripper_convention) differs from what the addressed end effector — or, with scene_gripper_convention, the simulated scene's environment — consumes; targets come from explicit gripper slots or slots expanded from action_contract.representation; an undeclared encoding on either side is a mismatch. Called by the runner when it resolves a skill and by openral sim run. (L631)
  • _check_license(manifest, *, commercial_use) -> None [@staticmethod] — Enforce license guards for the manifest's declared posture. (L829)
  • _validate_eval_jsons(skill_dir) -> None [@staticmethod] — Validate every <skill_dir>/eval/*.json against RSkillEvalResult. (L929)
  • _register(entry, registry_path) -> None [@staticmethod] (L955)
  • __repr__() -> str (L979)
  • resolve_rskill_local_dir(uri) -> Path | None — Return the absolute on-disk directory of an in-tree rSkill from a bare skill ref (name, rskills/<name>, or Hub repo id), or None for Hub-only refs. Used by openral benchmark run to write eval results and update rskill.yaml regardless of cwd. (L991)
  • _candidate_local_paths(uri) -> list[Path] — Enumerate on-disk candidates (cwd-relative + repo-root anchored) for a skill reference. Also unwraps HF Hub form <org>/rskill-<name> to in-tree rskills/<name>. (L1012)
  • discover_intree_rskills() -> list[tuple[str, RSkillManifest]] — Walk <repo>/rskills/*/rskill.yaml and return (name, manifest) pairs. Malformed entries are skipped with a stderr warning. (L1047)
  • find_repo_root_from(start) -> Path | None — Walk up from start for the first ancestor containing both pyproject.toml and rskills/. Public, re-exported from openral_rskill.__init__; used across the package boundary by openral_sim.cli, openral_cli.main, and the deploy_e2e launch file. (L1078)
  • SKILL_ONLY_EMBODIMENT_TAGS: frozenset[str] — {"any", "custom", "multi"}: embodiment tags valid on an rSkill manifest that no robot declares. (L1091)
  • intree_embodiment_tags() -> frozenset[str] — SKILL_ONLY_EMBODIMENT_TAGS plus every capabilities.embodiment_tags of the checkout's robots/*/robot.yaml; the typo guard for the open EmbodimentTag id (scaffolders + CI). Public, re-exported. (L1096)
  • known_benchmark_ids() -> frozenset[str] | None — benchmarks/*.yaml ∪ scenes/benchmark/*.yaml stems of the checkout, or None without one; the membership set for the open BenchmarkName id (writeback, benchmark scene guard, CI). Public, re-exported. (L1113)
  • _warn_unknown_benchmarks(manifest, *, source) -> None — rskill.unknown_benchmarks warning (never a failure) when a Hub manifest cites a benchmark the checkout lacks; called by rSkill.from_pretrained. (L1129)
  • validate_skill_ref(raw) -> str — Validate and return a bare rSkill reference unchanged (bare name, rskills/<name> path, or HF repo id); rejects a known URI scheme (hf://, local://, file://, http(s)://). Public, re-exported from openral_rskill.__init__. (L1141)
  • load_rskill_manifest(uri) -> RSkillManifest — Resolve a bare skill reference to a parsed manifest. Tries local path → in-tree mapping → HF Hub download. In-process memoised. (L1181)
  • resolve_rskill_to_hf(uri) -> str — Resolve a skill reference to either the underlying HF Hub repo id (hf://...) or an absolute local path (local://...); both forms are accepted by from_pretrained helpers. (L1253)
  • resolve_rskill_to_hf_with_revision(uri) -> tuple[str, str | None] — Like resolve_rskill_to_hf but splits the optional @<branch-or-sha> pin off an hf:// weights_uri into a separate revision, since HF drops a pin glued onto the repo id. A directory that already holds model.safetensors (or a sharded model.safetensors.index.json + shards) next to its rskill.yaml — the snapshot rSkill.from_pretrained leaves in ~/.cache/openral/rskills — resolves to itself rather than following its hf:// pointer, which otherwise re-resolved a just-downloaded repo through the default HF cache (second fetch online, hard failure offline). In-tree rskills/<name>/ dirs ship no weights and are unaffected. (L1287)

python/rskill/src/openral_rskill/hub_search.py

Free-text + facet search over an HF Hub org's rSkills — backs openral rskill search.

  • class HubRSkillHit(BaseModel) [frozen] — One matching Hub repo. (L43) fields: repo_id, manifest: RSkillManifest, local: Literal["in-tree","installed"] | None, hub_tags: tuple[str, ...]. local == "in-tree" compares the Hub repo_id against the name declared by a real rskills/<dir>/rskill.yaml — never against the fetched (possibly stale) remote manifest's own name field.
  • class HubRSkillSearchResult(BaseModel) — Search result: hits (sorted by repo_id), skipped_repo_ids — the explicit record of dropped repos, since this module never logs a skip itself — and inspected. (L82)
  • search_hub_rskills(query='', *, kind='', role='', embodiment='', license='', family='', org='OpenRAL', limit=None, max_workers=8) -> HubRSkillSearchResult — Lists every repo the org tagged rskill in one Hub call, fetches each candidate's rskill.yaml concurrently, then matches query locally (case-insensitive, every token substring-matched against id/name/description/tags) and applies the facet filters. limit caps hits after sorting by repo_id. (L207)
  • _manifest_haystack(repo_id, manifest, hub_tags) -> str — Build the lower-cased blob _matches_query searches. (L112)
  • _matches_query(query, haystack) -> bool — Every whitespace-split token must substring-match. (L127)
  • _matches_facets(m, *, kind, role, embodiment, license_, family) -> bool — Whether a manifest passes every non-empty facet filter. (L133)
  • _fetch_one(model) -> tuple[str, RSkillManifest | None, tuple[str, ...], str] — Worker-thread fetch + validate of one repo's rskill.yaml; narrow-catches expected errors so one bad repo never fails the whole search. The skip reason isn't logged (would corrupt --json | jq output) — only the repo id surfaces, via HubRSkillSearchResult.skipped_repo_ids. (L148)
  • _local_markers() -> tuple[frozenset[str], frozenset[str]] — (in_tree_names, installed_repo_ids) for the local marker. A corrupt registry or an invalid row is treated as no installed entries, with log.warning("rskill.hub_search.registry_unreadable", ...) — never silently swallowed. (L182)

python/rskill/src/openral_rskill/gpu_passthrough.py

GpuPassthroughSkill — minimal rSkill whose per-step image processing provably runs on GPU (M8 PR I/10).

  • _REDUCTION_SIZE: int = 64 — module constant; reduction-target size used to bound GPU latency. (L48)
  • _RGB_CHANNELS: int = 3 — module constant; channel count for the GPU mean-reduction read-back. (L52)
  • class GpuPassthroughSkill(rSkillBase) — Uploads each SensorFrame to torch.cuda, runs per-channel mean reduction (with explicit torch.cuda.synchronize), emits result as Action.confidence. Refuses silent CPU fallback. (L55)
  • __init__(sensor_id='wrist_rgb', n_joints=6, horizon=1, device='cuda', latency_budget_ms=None) (L76)
  • step_count -> int [@property] (L104)
  • on_load_weights() -> None — no-op (skill is weight-less). (L110)
  • on_quantize() -> None — no-op (skill is weight-less). (L114)
  • on_warmup() -> None — Allocate the GPU input buffer + launch a kernel so the first step doesn't pay cudaMalloc latency. (L118)
  • _configure_impl() — Lazy-import torch, resolve device, raise if cuda requested and torch.cuda.is_available() is False. (L144)
  • _activate_impl/_deactivate_impl/_shutdown_impl (L169)
  • _step_impl(world_state) -> Action — Pull frame → CPU→GPU upload → GPU reduction → action with confidence. (L185)
  • private: _extract_latest_image(world_state) -> NDArray[np.uint8], _gpu_reduce(frame, *, torch) -> (float, float, float). (L227 / L260)
  • _channels(encoding: FrameEncoding) -> int — Per-pixel channel count for a FrameEncoding; MONO8 → 1, BGR8/RGB8 → 3, everything else falls through to a defensive BGR default of 3. (L297)
  • _zero_frame() -> NDArray[np.uint8] — Resilient placeholder when no sensor frame is available yet. (L310)

python/rskill/src/openral_rskill/_diagnostics.py

Shared load-phase instrumentation seam — generalises the inline _heartbeat originally inside openral_sim.policies.pi05 so every VLA adapter's _build_* factory uses the same <prefix>_<name>_{start,heartbeat,done} event shape (CLAUDE.md §1.13 — single seam, no duplicates).

  • const _PAGE_SIZE = os.sysconf("SC_PAGE_SIZE") if hasattr(os, "sysconf") else 4096 — Page size in bytes, for converting /proc/self/statm's page count into rss_mb. (L46)
  • phase_timer(name, *, prefix="phase", interval_s=15.0, log=None, gpu_mb=False, **fields) -> Iterator[None] [@contextmanager] — Emits <prefix>_<name>_start/heartbeat/done events with elapsed_s, rss_mb, and major_faults so a slow-but-idle load is attributable to page reclaim. gpu_mb=True also attaches CUDA memory to the heartbeat; torch import is lazy so CPU-only hosts still work. (L136)
  • gpu_allocated_mb(*, no_import=False) -> float | None — Current CUDA allocator usage in MB (memory_allocated, not memory_reserved), or None if unavailable — the single GPU-memory probe eviction accounting and phase-timer heartbeats both read. no_import=True consults only an already-imported torch. Public, re-exported from openral_rskill.__init__. (L49)
  • _rss_majflt() -> tuple[float, int] | None — Process RSS (MB) + lifetime major-fault count from /proc/self/{statm,stat}; None off Linux. (L82)
  • const _PAGE_SIZE: int = os.sysconf("SC_PAGE_SIZE") if hasattr(os, "sysconf") else 4096 — Page size in bytes used to convert /proc/self/statm's page count into rss_mb. (L46)

python/rskill/src/openral_rskill/executor.py

Action-chunk executor shared by every chunked VLA family. Also the home of Real-Time Chunking (RTC): with an enabled lerobot RTCConfig the buffer becomes an ActionQueue whose tail a landing prefetch replaces.

  • const _CHUNK_TENSOR_RANK = 3 — Expected (batch, chunk, action_dim) tensor rank for a chunk producer's output. (L98)
  • _check_boundary_blend(steps, dims, *, chunk_size, prefetch_at, rtc_enabled, steps_name, dims_name) -> tuple[int, tuple[int, ...] | None] — Single source of the boundary-blend rules, shared by ChunkedExecutor.__init__ and build_chunk_executor (labels via steps_name / dims_name). steps an int >= 0, not with RTC, and < the shortest chunk that can enter behind a previous one: chunk_size, or with prefetch_at >= 1 the chunk_size - prefetch_at - 1 rows a merged chunk keeps (back to chunk_size when that is 0, since every merge is then fully stale); dims given only with steps >= 1, a non-empty list of distinct non-negative ints. ROSConfigError otherwise. (L101)
  • _check_rtc_relative(policy, *, rtc_enabled, rtc_prefix_fn, has_postprocess, name) -> None — Single source of the relative-action RTC guard, shared by ChunkedExecutor.__init__ and build_chunk_executor (which runs it before init_rtc_processor). With RTC enabled: ROSConfigError when policy.config.use_relative_actions is set and no rtc_prefix_fn re-anchors the prefix (the raw leftover is relative to the previous chunk's state, openral/openral#378), or when rtc_prefix_fn is given without postprocess_action (it reads the postprocessed, absolute rows). (L180)
  • _copy_action(action) -> Any — Independent copy of a NumPy / torch action (clone() / np.array(copy=True)); the stored boundary-blend tail. (L228)
  • _host_norm(value) -> float — L2 norm of a NumPy / torch action for any float dtype (torch reduces via .float(), so bfloat16 works; NumPy in float64); the DEBUG jump_norm. (L233)
  • class ChunkedExecutor — Owns the per-step action buffer for every chunked VLA family, optionally overlapping chunk N+1 inference with execution via a background thread; calls predict_action_chunk directly rather than resetting lerobot's shared queue. Default producer is policy.predict_action_chunk, or a custom chunk_fn; prefetch_at=0 is synchronous. postprocess_action finishes each chunk where it is produced: one host copy of the whole chunk (any non-CPU device — CUDA, MPS, XPU), then the per-action postprocessor on every row, in the producing (prefetch) thread, so pops never touch the GPU and lerobot's AbsoluteActionsProcessorStep adds the state cached for that chunk's own inference; under RTC it runs at merge time into the ActionQueue's processed copy, which pops serve as NumPy, while the raw rows stay for the guidance blend. Without RTC a pre-fetched chunk is appended behind the tail minus its leading actions that are already in the past (_merge_prefetched): it was planned from the observation of the step that launched it (action i targets that step + i), and the tail served those steps meanwhile; a fully stale chunk is discarded and the step replans synchronously. RTC mode (an enabled lerobot RTCConfig) swaps the buffer for an ActionQueue and replaces its tail on each prefetch landing instead of appending, requiring prefetch_at >= 1 for real overlap. The guidance prefix is the raw leftover rows (truncated to execution_horizon) — or, with rtc_prefix_fn (relative-action policies, see relative_rtc_prefix_fn), the postprocessed absolute leftover re-anchored (in the prefetch thread, so a failure surfaces through the background-error path) to the state of the batch the prefetch was built from; a use_relative_actions policy without it is refused (_check_rtc_relative). Every merge logs chunked_executor.rtc_merge at DEBUG with chunk_index, merge_tick (served actions before the merge; action merge_tick + 1 is the first from the new chunk) and the measured rtc_delay. Boundary blend (opt-in boundary_blend_steps K, non-RTC only, openral/openral#378): when a chunk enters behind a previous one (prefetch merge after the stale-row drop, or a synchronous re-plan), delta = last_served - new[0] is added to its first K rows with weight 1 - (i+1)/(K+1) on blend_dims (default all), so the boundary step is 1/(K+1) of the raw jump and row K onward is the policy's own (K must leave row K inside the entering chunk); runs on the robot-facing actions (NumPy or torch, any float dtype incl. bfloat16), stitches to a copy of the last served action (callers may mutate what they get), leaves dims outside blend_dims as produced (non-finite values included, never turned into NaN), never blends the first chunk after construction/reset(), and is refused together with RTC. (L244)
  • __init__(policy=None, *, chunk_fn=None, chunk_size=None, prefetch_at=20, rtc_config=None, postprocess_action=None, boundary_blend_steps=0, blend_dims=None, rtc_prefix_fn=None) — Takes policy OR chunk_fn+chunk_size (ValueError otherwise). Negative prefetch_at raises ROSConfigError; it's clamped to chunk_size - 1 at construction. An enabled rtc_config requires a clamped prefetch_at >= 1 (ROSConfigError otherwise); a lead shorter than execution_horizon warns rather than errors. boundary_blend_steps / blend_dims are checked by _check_boundary_blend: steps an int >= 0 and < chunk_size, and with a pre-fetch also < chunk_size - prefetch_at - 1 (the rows a merged chunk keeps after its stale-row drop; when that is 0 every boundary is a synchronous re-plan and the bound is chunk_size); blend_dims a non-empty list of distinct non-negative ints given only with steps >= 1; blending + RTC raises ROSConfigError. A blend_dims index past the action width raises ROSConfigError when the first chunk lands, before any action is served (the robot-facing width is only known after postprocessing). (L247)
  • start() -> None — Mark as running (call after policy is on-device). (L441)
  • stop() -> None — Signal background thread, join. (L445)
  • reset() -> None — Clear buffer/bg state, the boundary-blend tail, and the RTC queue + last delay; policy.reset() when a policy was given. (L452)
  • select_action(batch_or_fn) -> Any — Next action; cold-start foreground inference / buffer pop / wait-on-prefetch. Delegates to _select_action_rtc in RTC mode. (L472)
  • private: _pop_and_maybe_prefetch(batch) (every pop routes through it so the trigger is branch-independent), _produce(payload, chunk_index, kind, rtc_kwargs=None) (passes synchronize=True on the prefetch path only), _materialize, _extend_buffer, _launch_prefetch(batch), _blend_boundary(actions) (every entering chunk passes through it: checks action type and blend_dims width, then fades the new chunk's offset from the last served action over its first K rows; DEBUG chunked_executor.boundary_blend with jump_norm and steps)
  • private (RTC): _select_action_rtc(batch) — serve from the ActionQueue, re-checking after the prefetch wait so a landed chunk isn't discarded. _rtc_merge(chunk, *, idx_before, served_before) — replace the tail; real_delay is the index delta the queue advanced during inference, and the logged merge_tick is served_before (served count at launch) + real_delay, and the producer's batch dim must be 1. _raise_bg_error_if_any() — re-raise a latched background error on the foreground thread. The prefetch path truncates the leftover tail to execution_horizon rows so tail length doesn't depend on how early prefetch_at fires.

python/rskill/src/openral_rskill/execution_budget.py

Execution budgets measured on the graph clock (sim seconds under use_sim_time) with wall-clock backstops, shared by the rSkill runner's goal budget and ROSActionRskill's result wait. Without a graph clock the budget is time.monotonic() exactly as before — never the NTP-stepped ROS system clock. Adapted from DsslRobot/openral.

  • const DEFAULT_WALL_CAP_FACTOR = 20.0 — a graph-clock budget may stretch to at most this many times its value in wall seconds: intact down to 0.05x real time (the slowest measured rendering graph, 0.09x, needs 11.1x). (L66)
  • const DEFAULT_CLOCK_STALL_S = 10.0 — wall seconds the graph clock may stand still before the goal is failed. (L69)
  • const PARAM_WALL_CAP_FACTOR = "execution_wall_cap_factor" — ROS parameter name a host node declares to override DEFAULT_WALL_CAP_FACTOR; read by node_execution_budget. (L73)
  • const PARAM_CLOCK_STALL_S = "graph_clock_stall_s" — ROS parameter name a host node declares to override DEFAULT_CLOCK_STALL_S; read by node_execution_budget. (L74)
  • class BudgetMiss(BaseModel) — Frozen Pydantic record of why a budget lapsed: kind ("deadline_exceeded" | "clock_stalled", the failure_reason prefix), elapsed_s on the budget's clock, and a detail string. (L77)
  • class ExecutionBudget — One goal's execution budget; starts at construction. (L97)
  • __init__(budget_s, *, graph_now=None, wall_cap_factor=DEFAULT_WALL_CAP_FACTOR, stall_s=DEFAULT_CLOCK_STALL_S, wall_now=time.monotonic) — graph_now is a graph-clock seconds callable (see graph_clock_fn) or None for wall time; budget_s <= 0 disables every check. Raises ROSConfigError on a non-finite budget_s (a NaN would never lapse) and, on a graph-clock budget, on a non-positive / non-finite backstop — the backstops cannot be disabled (deadline fallback is mandatory). (L117)
  • on_graph_clock -> bool [@property] (L150)
  • elapsed_s() -> float — Seconds on the budget's clock; 0 while a sim node has not yet received its first /clock (the budget anchors on the first non-zero reading, so that message is never a jump that lapses it). A backward /clock jump (sim reset) banks the elapsed so far and re-anchors, so elapsed neither freezes nor rewinds. (L171)
  • progress() -> float — elapsed / budget clamped to [0, 1]. (L179)
  • check() -> BudgetMiss | None — deadline_exceeded when elapsed > budget (either clock) or wall elapsed > wall_cap_factor x budget; clock_stalled when the graph clock has not advanced (or never started) for stall_s of wall time. (L183)
  • graph_clock_fn(node) -> Callable[[], float] | None — The rclpy node's clock as a seconds callable when its use_sim_time parameter is true at call time, else None (also for node=None or an undeclared parameter); re-read per call so a node switched off sim time goes back to time.monotonic(). (L222)
  • node_execution_budget(node, budget_s, *, graph_clock=True) -> ExecutionBudget — An ExecutionBudget on node's clock (graph clock when graph_clock and the node is on sim time now), with the backstops taken from the node's execution_wall_cap_factor / graph_clock_stall_s parameters when declared, else the defaults — the one builder the rSkill runner and ROSActionRskill share. (L245)

python/rskill/src/openral_rskill/ros_action_rskill.py

ROS-wrapping rSkill adapter — bridges arbitrary ROS 2 action / service servers (MoveIt, Nav2, …) into the rSkillBase lifecycle. Selected by make_default_skill_resolver when manifest.kind in {"ros_action", "ros_service"}.

  • const _RESULT_DEADLINE_MULTIPLIER = 5.0 (L84)
  • const _MIN_RESULT_DEADLINE_S = 2.0 (L89)
  • const _FUTURE_POLL_INTERVAL_S = 0.02 (L93)
  • const _FINAL_WAYPOINT_TOL_RAD = 0.05 — worst per-joint error at which the trajectory replay counts the final waypoint as reached (justification in the source comment). (L112)
  • const _REPLAY_SETTLE_TIMEOUT_S = 10.0 — bound past the plan's last time_from_start for the measured joints to reach the final waypoint before ROSDeadlineMissed. (L117)
  • const _WAIT_FOR_SERVER_TIMEOUT_S = 15.0 (L125)
  • const _GOAL_STATUS_LABELS: dict[int, str] = {...} — Human-readable labels for action_msgs/GoalStatus codes, used in error/log messages. (L132)
  • build_joint_permutation_from_names(*, source_names, target_names) -> list[int] — Build the permutation that reorders a wrapped server's JointTrajectory.positions into the host RobotDescription.joints order. Raises ROSConfigError on set-inequality so a joint mismatch surfaces loudly instead of silently swapping bytes. (L203)
  • const CUMOTION_PIPELINE_ID = "isaac_ros_cumotion" — the cuMotion MoveIt planning-pipeline id. (L172)
  • maybe_inject_cumotion_pipeline(goal_dict, *, interface_type, capabilities) -> dict — On a host clearing the cuMotion GPU floor, sets request.pipeline_id = CUMOTION_PIPELINE_ID on a MoveGroup goal so MoveIt plans with cuMotion; no-op for non-MoveGroup actions, low-VRAM hosts, an already-set pipeline_id, or a goal with no request block. Pure — never mutates the input. (L175)
  • class ROSActionRskill(rSkillBase) — rSkillBase shim wrapping a ROS 2 ActionClient (or service client), in two modes selected by manifest.ros_integration.result_trajectory_field: trajectory mode replays one waypoint per step(), paced by the plan's time_from_start on the host node's clock, and raises ROSRskillGoalSatisfied only once the measured joints reach the last waypoint (ROSDeadlineMissed if they do not settle in time); result-only mode awaits the wrapped result and raises ROSRskillGoalSatisfied. ROS imports are deferred to _configure_impl so the module imports cleanly without ROS sourced. (L330)
  • __init__(*, manifest, ros_node, robot_description, prompt, prompt_metadata_json) (L363)
  • _configure_impl() — Lazy-import IDL, build ActionClient/service client, parse default_goal_json. (L442)
  • _activate_impl() — no-op; the wrapped action dispatches on first step(). (L532)
  • _deactivate_impl() / _shutdown_impl() — Release the wrapped client. (L535)
  • _step_impl(world_state) -> Action — First call sends goal and caches result; subsequent calls emit the waypoint due by time_from_start (timed on node_execution_budget(self._node, planned + _REPLAY_SETTLE_TIMEOUT_S)), holding the last until world_state.joint_state is within _FINAL_WAYPOINT_TOL_RAD. (L552)
  • _final_waypoint_error(world_state) -> float | None — worst |measured - final| over the planned joints, by name; None (never "reached") when unmeasurable. (L625)
  • _poll_future(future, *, deadline_s, what, graph_clock=True) — Polls a wrapped future under node_execution_budget(self._node, …): on the graph clock when graph_clock and the host node runs on sim time, else on time.monotonic(). Raises the typed ROSDeadlineMissed (FAILURE_DEADLINE_MISSED) naming deadline_exceeded / clock_stalled; a non-positive deadline_s lapses at once (fail closed). Only the result-only result wait (Nav2: the server moves the robot) uses the graph clock — goal acceptance, service responses and a MoveIt plan result are compute/IPC and pass graph_clock=False; a lapsed result wait cancels the wrapped goal before re-raising. (L903)

python/rskill/src/openral_rskill/look_at_rskill.py

Camera-aiming MoveGroup skill. Selected by make_default_skill_resolver when manifest.ros_integration.goal_builder == "look_at" (new RosIntegration.goal_builder field; RSkillAction gains LOOK = "look").

  • const _DEFAULT_CAMERA = "wrist" (L46)
  • const _DEFAULT_POSITION_TOLERANCE_M = 0.02 (L47)
  • const _DEFAULT_ORIENTATION_TOLERANCE_RAD = 0.15 (L48)
  • const _TF_LOOKUP_TIMEOUT_S = 5.0 (L49)
  • const _XYZ_LEN = 3 (L50)
  • const _MIN_DIRECTION_NORM = 1e-9 (L51)
  • resolve_camera_sensor(description, camera) -> SensorSpec — Find the named camera in RobotDescription.sensors; ROSConfigError listing the available sensor names on a miss (explicit beats implicit — default camera is "wrist"). (L54)
  • build_look_at_constraints(*, camera_goal: Pose6D, link_name, link_t_cam=None, position_tolerance_m=0.02, orientation_tolerance_rad=0.15) -> dict — Lower a camera gaze pose into one MoveGroup goal_constraints entry by delegating to pose_goal_rskill.build_pose_constraints with the optical (z) axis tolerance set to π (roll free). With link_t_cam the goal is re-expressed for the mount link; without it the camera frame is the constrained link. (L100)
  • class LookAtRskill(ROSActionRskill) — Consumes the merged goal's look_at block (target_xyz required, plus frame_id/camera/standoff_m/tolerances) instead of raw constraints, lowering it lazily on the first step() via compute_gaze_pose → build_pose_constraints. The manifest ships plan_only: true so MoveIt-side execution never bypasses the safety kernel. (L132)

python/rskill/src/openral_rskill/pose_goal_rskill.py

Generic Cartesian end-effector pose MoveGroup skill. Selected by make_default_skill_resolver when ros_integration.goal_builder == "pose". Home of the shared pose→constraints lowering LookAtRskill reuses.

  • const _SOLID_PRIMITIVE_SPHERE = 2 — shape_msgs/SolidPrimitive.SPHERE. (L40)
  • const _DEFAULT_POSITION_TOLERANCE_M = 0.01 (L41)
  • const _DEFAULT_ORIENTATION_TOLERANCE_RAD = 0.05 (L42)
  • const _XYZ_LEN = 3 (L43)
  • const _QUAT_LEN = 4 (L44)
  • const _QUATERNION_ORDERS = ("xyzw", "wxyz") (L45)
  • const _TF_LOOKUP_TIMEOUT_S = 5.0 (L46)
  • build_pose_constraints(*, pose: Pose6D, link_name, link_t_target=None, position_tolerance_m=0.01, orientation_axis_tolerances_rad=(0.05, 0.05, 0.05)) -> dict — Lower a target pose into one MoveGroup goal_constraints entry (sphere position region + per-axis orientation constraint); link_t_target re-expresses the goal for the constrained link. The one place pose→MoveGroup-constraint math lives — do not re-implement. (L49)
  • pose_from_block(block) -> tuple[Pose6D, str, float, float] — Parse a pose goal block → (pose, link_name, pos_tol, orient_tol). Orientation is a 4-float quaternion array; component order from block["quaternion_order"] ("xyzw" default / "wxyz"). ROSConfigError on missing/ill-typed fields or an unknown order. (L141)
  • class PoseGoalRskill(ROSActionRskill) — Consumes the merged goal's pose block; lowers it via build_pose_constraints (full orientation) on the first step(), then dispatches/replays like the parent. link_t_target is identity in v1 (the RobotDescription tool-frame offset is a later phase). (L198)

python/rskill/src/openral_rskill/joint_goal_rskill.py

Joint-space MoveGroup skill. Selected when ros_integration.goal_builder == "joint". The LLM-facing replacement for hand-written joint_constraints JSON.

  • const _DEFAULT_JOINT_TOLERANCE_RAD = 0.001 (L23)
  • const _JOINT_WEIGHT = 1.0 (L24)
  • joint_constraints_from_block(block) -> dict — Lower a joint block (joint_names, positions, optional position_tolerance_rad) into one goal_constraints entry ({"joint_constraints": [{joint_name, position, tolerance_above, tolerance_below, weight}, …]}). ROSConfigError on missing/ill-typed fields or a name/position length mismatch. (L27)
  • class JointGoalRskill(ROSActionRskill) — Consumes the merged goal's joint block; lowers it into a joint_constraints goal at _configure_impl, then dispatches/replays like the parent. (L69)
  • _configure_impl() -> None (L77)

python/rskill/src/openral_rskill/_vla_core.py

Shared helpers for VLA adapters (Layer 3); internal — no public re-export.

  • InferenceKind — Re-export of openral_observability.InferenceKind (Literal["foreground", "prefetch", "single"]), the closed value set for the inference.kind label — a timing axis, deliberately without a "chunk" value since chunk shape rides inference.chunk_size/chunk_index instead. (L45)
  • resolve_device(spec: VLASpec) -> str — "auto" → "cuda:0" / "mps" / "cpu" against real torch. (L48)
  • resolve_rskill_repo_id(weights_uri: str, *, adapter_name: str) -> str — Validate skill reference and resolve to bare HF repo id; adapter_name is used in the ROSConfigError message. (L73)
  • resolve_rskill_repo_revision(weights_uri: str, *, adapter_name: str) -> tuple[str, str | None] — Like resolve_rskill_repo_id but also returns the optional @<sha> revision pin, threaded into from_pretrained/snapshot_download by the sim adapters, and warns rskill.unpinned_weights when an hf:// skill is unpinned. (L104)
  • resolve_image_preprocessing(manifest, spec_extra) -> ImagePreprocessing — Build the ImagePreprocessing block an adapter applies, by strict precedence: spec_extra YAML override → manifest.image_preprocessing per-checkpoint contract → schema defaults. No auto-derivation from policy.config.input_features — missing hints surface as the schema default rather than a guessed heuristic. Raises ROSConfigError when a manifest alias key is not a slot declared by sensors_required[].vla_feature_key. (L147)
  • resolve_state_dim(manifest, spec_extra) -> int | None — Per-checkpoint proprio state dimension: spec_extra["state_dim"] override → manifest.state_contract.dim → None (adapter falls back to the policy's own preprocessor width). (L252)
  • resolve_camera_keys(manifest, spec_extra, *, scene_cameras=None, default=("camera1", "camera2")) -> tuple[str, ...] — Resolve which scene camera keys an adapter pulls from the observation: spec_extra["camera_keys"] override → scene_cameras (the SimEnvironment YAML's scene.cameras) → default. (L273)
  • manifest_camera_slots(manifest) -> tuple[str, ...] — The RGB VLA slots an rSkill declares, in sensors_required order (vla_feature_key suffix). The resolve_camera_keys default for adapters whose cameras are the manifest's slots (openvla, rldx, lingbot, diffusion). (L305)
  • checkpoint_image_keys(image_preprocessing, camera_keys) -> tuple[str, ...] — The one slot→checkpoint rename: input_template.format(cam=aliases.get(slot, slot)) per slot. Used by the xvla / gr00t / diffusion / lingbot / molmoact2 adapters so manifest aliases is the only rename mechanism. (L326)
  • resolve_n_action_steps(manifest, extra, *, default, chunk_size=None) -> int — The one chunk-slicing rule every policy adapter calls (xr1, xvla, gr00t, lingbot_vla/vla2, rldx, molmoact2, openvla, and via apply_chunk_replay smolvla/pi05/act): extra["n_action_steps"] (--n-action-steps) > manifest.n_action_steps > deprecated extra["replan_steps"] (warns vla.deprecated_replan_steps) > default; bounded by chunk_size (else manifest.chunk_size, logs vla.n_action_steps_clamped); ValueError below 1. (L354)
  • apply_chunk_replay(policy, spec_extra, *, manifest=None, default_n_action_steps=None) -> int — Set policy.config.n_action_steps via resolve_n_action_steps, defaulting to and bounded by the policy's own config.chunk_size; used by the lerobot-style adapters (smolvla, act, pi05, xvla). (L405)
  • _CUDAGRAPH_COMPILE_MODES — frozenset({"reduce-overhead", "max-autotune"}); the torch.compile modes that may capture CUDA graphs and therefore require output cloning (static replay buffers would otherwise be overwritten under lerobot's queued action views / ChunkedExecutor pre-fetch). (L460)
  • _has_bnb_quantized_modules(policy) -> bool — True when any submodule's class comes from bitsandbytes (Linear4bit / Linear8bitLt rewrites from openral_sim._quantization). Class-module-path check; never imports bnb. (L472)
  • _clone_chunk_output(out, torch) -> Any — Recursively .clone() every tensor in a chunk forward's output (tensor / tuple / list / dict; non-tensor leaves pass through) so downstream holders own their storage, detached from CUDA-graph static buffers. (L486)
  • maybe_compile_chunk_forward(policy, spec_extra, device, torch, *, method_name="_get_action_chunk") -> bool — Best-effort torch.compile of the chunk forward with a runtime fallback that latches into eager mode on backend errors; skipped on CPU or when vla.extra.compile is falsy. Never compiles bitsandbytes-quantized policies (dtype-mismatch under mixed nf4/bf16), and routes cudagraph-mode output through _clone_chunk_output on both branches. (L504)
  • run_inference(policy, batch, *, chunk_index=None, kind="single", chunk_size=None, engine=None, call=None, call_kwargs=None, synchronize=False) -> Tensor — Single seam wrapping a policy inference call in inference_span + torch.no_grad() — the only place inference.kind/chunk_index/chunk_size/inference.engine attributes are emitted. call replaces the default policy.select_action(batch) for custom chunk producers; ChunkedExecutor passes synchronize=True on its prefetch so the span times compute, not just kernel launch. Must stay torch.no_grad(), never torch.inference_mode() — RTC's guidance calls autograd.grad, which raises under inference mode. (L607)
  • resolve_inference_engine(owner, declared=None) -> str — Resolve the backend actually executing after optional runtime attachment: explicit _openral_inference_engine marker wins, then the openral-pro-trt module, then the manifest name (normalized pytorch→torch/tensorrt→trt). Keeps the dashboard from reporting a stale pre-attachment runtime. (L696)
  • _RTC_ADAPTERS — frozenset({"smolvla", "pi05"}); the flow-matching adapters whose lerobot policies carry an rtc_config. molmoact2 / pi0_fast support RTC upstream but are out of scope — extend this set and the adapter's chunk_fn kwargs pass-through together. (L736)
  • const _RTC_KEYS — frozenset({"enabled", "execution_horizon", "max_guidance_weight", "prefix_attention_schedule", "debug"}); the closed key set _parse_rtc_config accepts in a policy_extras.rtc block. (L743)
  • _parse_rtc_config(spec_extra, *, adapter_name, default_horizon=10) -> RTCConfig | None — Parse the manifest's policy_extras.rtc block into a lerobot RTCConfig, or None if absent. ROSConfigError on a non-mapping block, an unknown key or schedule, a non-positive horizon, RTCConfig rejection, or an adapter outside _RTC_ADAPTERS; lerobot imports are deferred. (L748)
  • rtc_enabled_in_extra(spec_extra, *, adapter_name) -> bool — True only for a present, well-formed, enabled rtc block — keyed on the parsed enabled flag, not just presence, so rtc: {enabled: false} still compiles. Lets a factory (e.g. smolvla skipping maybe_compile_chunk_forward) decide before the executor exists. (L818)
  • const _CHUNK_BOUNDARY_BLEND_KEYS — frozenset({"steps", "dims"}); the closed key set of a policy_extras.chunk_boundary_blend block.
  • _parse_chunk_boundary_blend(spec_extra, *, adapter_name) -> tuple[int, tuple[int, ...] | None] — Parse the YAML shape of policy_extras.chunk_boundary_blend: {steps: K, dims: [...]} into (steps, dims); (0, None) without a block. ROSConfigError on a non-mapping block, unknown keys, steps not an int >= 1, or dims missing or not a list (no all-dims default in a manifest: a binarized gripper or delta-action dim must not be faded). The rules themselves are executor._check_boundary_blend's, applied by build_chunk_executor under this block's key.
  • build_chunk_executor(spec_extra, *, policy=None, chunk_fn=None, chunk_size=None, adapter_name="policy", postprocess_action=None, rtc_prefix_fn=None) -> ChunkedExecutor | None — Shared executor construction: chunk_prefetch enables overlap, chunk_prefetch_at calibrates the lead (default 20, clamped to the chunk). An enabled policy_extras.rtc block sets policy.config.rtc_config and calls init_rtc_processor() before building the executor; a block without execution_horizon gets the clamped chunk_prefetch_at (guidance over the whole overlap); RTC is refused with ROSConfigError (never a silent downgrade) on chunk size 1, chunk_prefetch: false, a policy without init_rtc_processor, bitsandbytes-quantized weights, or a use_relative_actions policy without rtc_prefix_fn (checked before the policy is mutated). With no rtc block, construction and served actions are byte-identical to before. A policy_extras.chunk_boundary_blend block is checked with executor._check_boundary_blend (before any RTC policy mutation) and handed to the executor as boundary_blend_steps / blend_dims; it is refused with ROSConfigError on chunk size 1, a steps not below the chunk size (or, with chunk_prefetch, below n - chunk_prefetch_at - 1), or alongside an enabled rtc block. (L897)
  • relative_rtc_prefix_fn(preprocessor, *, device) -> Callable[[Tensor], Tensor] | None — The rtc_prefix_fn for a relative-action checkpoint (an enabled RelativeActionsProcessorStep in its preprocessor), else None. Mirrors lerobot 0.6.0's RTC rollout: absolute leftover rows → lerobot.policies.rtc.reanchor_relative_rtc_prefix against the relative step's cached state (the latest preprocessor call) → the preprocessor's normalizer, on device. The hook raises ROSRuntimeError when no state is cached, where lerobot would silently keep the stale rows; building it raises ROSConfigError when no normalizer carries action stats (the prefix would reach the model in raw units). pi05 and SmolVLA pass it to build_chunk_executor. (L1080)
  • to_numpy_action(action_tensor) -> NDArray[np.float32] — (1, A) torch tensor → 1-D float32 NumPy. (L1151)
  • release_torch_modules(owner, *attrs, device="", torch=None) -> None — Drop an adapter's references to its loaded torch modules, then gc.collect() and (on CUDA) torch.cuda.empty_cache() — order matters: empty_cache() only returns already-free blocks, so it must run after the reference is dropped, and gc.collect() is needed since a policy is typically part of a reference cycle. Best-effort; called from close() in every sim VLA adapter. (L1569)
  • parse_hf_file_uri(uri: str) -> tuple[str, str | None, str] — Splits hf://owner/repo[@rev]/path/to/file.ext into (repo_id, revision, filename) for per-file hf_hub_download calls. Rejects bare-repo URIs with a typed ROSConfigError. (L1279)
  • materialize_processor_dir(manifest: RSkillManifest) -> str — Downloads the manifest's per-file processors artefacts via hf_hub_download and symlinks them under lerobot-canonical filenames in a fresh temp directory, also fetching each processor JSON's sibling state_file so PolicyProcessorPipeline.from_pretrained resolves every step locally. Used by the SmolVLA and modern-ACT adapters; raises ROSConfigError if manifest.processors is None. (L1329)
  • hf_download_cached_first(hf_hub_download, local_not_found_exc, *, repo_id, filename, revision=None, **extra) -> str — Cache-first wrapper around huggingface_hub.hf_hub_download: tries local_files_only=True first, falling back to a normal call on LocalEntryNotFoundError. Avoids the per-file HEAD validation that turns a cached load into seconds of latency on a cold connection; HF_HUB_OFFLINE=1 extends the skip to inner lerobot/transformers calls this helper doesn't wrap. Public, re-exported from openral_rskill.__init__. (L1167)
  • local_snapshot_dir(repo_id, *, revision=None, ignore_patterns=("*.md",), **extra) -> str (L1230) — The resolved checkpoint as a local directory: a directory (an installed rSkill snapshot, which resolve_rskill_to_hf_with_revision returns for a snapshot holding model.safetensors or a sharded index) is returned as is, a Hub repo id is snapshot_downloaded with ignore_patterns / extra forwarded. snapshot_download rejects a filesystem path as a repo id, so ACT, Diffusion, GR00T and the processor-sidecar fallback all broke on an installed skill until they routed through this (2026-09-23). Public, re-exported from openral_rskill.__init__.
  • suppress_hf_weight_init() -> Iterator[None] [@contextmanager] — Context manager that patches transformers.PreTrainedModel._init_weights to a no-op, skipping SmolVLA's full random backbone init when the checkpoint sets load_vlm_weights=False. Only sound when the checkpoint covers every parameter — pair with assert_all_parameters_finite. Process-global while active; the skill runner serialises loads behind its resident-skill lock. (L1624)
  • assert_all_parameters_finite(policy, *, repo_id) -> None — Raises ROSConfigError if any floating-point parameter is NaN/Inf — the guard that makes suppress_hf_weight_init safe, since uninitialised memory read as float is overwhelmingly non-finite. (L1698)
  • call_make_processors_cached_first(make_pre_post_processors, policy_config, *, pretrained_path, **kwargs) -> tuple[Any, Any] — Wraps lerobot's make_pre_post_processors so the tokenizer's AutoTokenizer.from_pretrained call skips its HF HEAD revalidation when that tokenizer is already cached, by flipping HF_HUB_OFFLINE for the inner call. Passthrough on a cold cache or for adapters with no tokenizer step (ACT, Diffusion Policy). (L1479)
  • private: _read_tokenizer_repo_from_preprocessor(pretrained_path) -> str | None — Parses <pretrained_path>/policy_preprocessor.json for the tokenizer_processor step's config.tokenizer_name. Returns None on missing/malformed JSON or absent step (ACT / Diffusion Policy). Used by call_make_processors_cached_first.
  • private: _hf_tokenizer_is_cached(repo_id) -> bool — Probes huggingface_hub.try_to_load_from_cache(repo_id, "tokenizer_config.json") and returns True only when the result is a real cached path (str), not None or the _CACHED_NO_EXIST sentinel. Returns False on any import error so callers fall back to a normal online load.

python/rskill/src/openral_rskill/_policy_io.py

The one policy <-> robot I/O codec (joint order, deg<->rad, gripper scale, clamp), applied on every deploy dispatch path; internal — no public re-export.

  • class PolicyIOCodec(BaseModel) — Frozen codec built once per skill from the manifest's action_contract (joint_units, joint_names, gripper_scale, legacy policy_extras.gripper_scale with a deprecation warning) + the RobotDescription. State: permute robot→policy order, then gripper * gripper_scale / degrees(joint). Action: gripper / gripper_scale / radians(joint) back in robot order; on the slot path the same per-channel conversion in policy order per slot (JOINT_POSITION/JOINT_VELOCITY + GRIPPER_POSITION), no permutation, and JOINT_POSITION slot channels clamped by the same _CLAMP_EPS as clamp — keyed by the slot's joint_names, or RobotDescription.joints order when it declares none (this replaced the runner's own _clamp_joint_position_slice, which skipped unnamed slots); a GRIPPER_POSITION slot's channel clamped strictly inside its ee's kernel bound (gripper_ranges, from EndEffectorSpec.resolved_command_range), a move wider than the epsilon logged once per end effector (policy_io.gripper_command_clamped). (L101)
  • from_manifest(manifest, description, *, adapter=None) -> PolicyIOCodec — Joint order: action_contract.joint_names (robot names in policy order; unknown name → ROSConfigError) > the adapter's policy.config.action_feature_names (.pos/_joint_ normalised; mismatch → identity + policy_io.feature_names_unmatched) > identity. ROSConfigError for a joint-position contract without joint_units or a legacy extras scale contradicting the contract. (L144)
  • is_identity -> bool [@property] — True when the codec changes nothing (radians, gripper_scale == 1, robot joint order); openral_sim.sim_runner refuses a non-identity codec on a scene without converts_policy_units. (L284)
  • to_policy_state(robot_state) -> NDArray — Robot-order radians → policy order + units. (L303)
  • to_robot_action(policy_action, *, slots=None) -> NDArray — Policy action → robot units (+ robot order without slots; + JOINT_POSITION limit clamp with slots). (L316)
  • clamp(robot_action) -> NDArray — Pull each joint strictly (1e-3) inside RobotDescription position limits; no-op on a width mismatch. (L404)
  • private: _effective_perm(robot_to_policy, n) -> list[int] — Identity unless a valid length-n permutation, so unit conversion runs on the no-reorder path too. (L60)

python/rskill/src/openral_rskill/testing.py

Test helpers for skill latency-budget enforcement (CLAUDE.md §2).

  • class LatencyBudgetExceededError(AssertionError) — Raised by assert_within_budget on overrun; a plain AssertionError subclass (not ROSRuntimeError) so pytest reports a crisp failure with the budget delta rather than the operational exception the safety supervisor would catch. (L42)
  • __init__(*, stage, measured_ms, budget_ms, rskill_id=None) — Builds the failure message and stores stage/measured_ms/budget_ms/rskill_id on the instance. (L56)
  • assert_within_budget(*, measured_ms, budget: RSkillLatencyBudget, stage="per_chunk", rskill_id=None, tolerance_pct=0.0) -> None — Assert measured_ms does not exceed budget's field for stage (per_chunk_ms/warmup_ms/load_ms), with an optional percent tolerance. Unset optional budget stages (warmup_ms/load_ms are None) pass silently — enforcement is opt-in per manifest. Raises LatencyBudgetExceededError on overrun, ValueError on a negative input. (L78)

python/rskill/src/openral_rskill/_lerobot_compat.py

Compatibility shim for lerobot.policies import side-effects.

  • const _STUB_NAME = "lerobot.policies.groot.modeling_groot" (L32)
  • private: _install_stub() -> None (L35)
  • sanitize_smolvla_config(repo_id, *, revision=None) -> None — Strip config keys lerobot's SmolVLAConfig rejects (e.g. pretrained_revision) from a checkpoint's config.json, in place, before SmolVLAPolicy.from_pretrained. Idempotent; no-op when the config is already clean or lerobot/the config are unavailable. (L54)