Tools
Part of the OpenRAL public-symbol inventory. Hand-curated;
(LNN)markers are refreshed bytools/refresh_methods_linenos.py.
tools/profile_policy_load.py
One-shot wall-time breakdown of a single policy load. Drives openral_sim.factory.make_policy against an in-tree rSkill manifest and prints a phase-by-phase summary built from every <prefix>_<name>_{start,done} event emitted by openral_rskill._diagnostics.phase_timer. Use when ros2 launch openral_rskill_ros …_e2e.launch.py or openral sim run is slow to first action — answers "where do the seconds go" before changing any code.
class _PhaseCapture—structlogprocessor that buffers_start/_doneevents and pairs them by name. Insertion-ordered so the rendered table mirrors the actual load order. (L49)_parse_args(argv) -> argparse.Namespace—--rskill <dir>(required),--device(defaultauto). (L88)_build_env_cfg(rskill_dir, *, device) -> _SimpleEnvCfg— Builds a minimal env_cfg from<rskill_dir>/rskill.yaml; mirrorsrskill_runner_node._SimpleEnvCfg. (L122)_render(pairs, total_s) -> str— Formats the captured pairs asphase / elapsed_s / sharecolumns plus an(unaccounted)row when phase coverage misses >1 s. (L141)main(argv=None) -> int— Late-importsopenral_sim.factory.make_policyso the import cost lands inside the profiled total; reportsHF_HUB_OFFLINEstatus alongside the result.
tools/schema_export.py
Generates JSON Schema files for every public openral_core model.
_enum_schema(cls) -> dict[str, Any]— Minimal JSON Schema for astrEnum. (L130)export_schemas(out_dir=_OUT_DIR) -> dict[str, Any]— Export JSON Schema for every public model. (L142)check_drift(out_dir=_OUT_DIR) -> bool— On-disk schemas == regenerated. (L195)
tools/audit_sim_configs.py
Real GPU rollout audit for every YAML under scenes/. Operator-driven (not a pytest test); 1 episode per config; writes outputs/audit_sim_configs.json and prints a Markdown table. See just sim-audit. Two modes: default (full rollout for sim/benchmark, Tier-2 launch + SIGINT for deploy) and --check-compatibility (cheap in-process scene+rSkill+HAL gate, no subprocess / no GPU).
DEFAULT_TIMEOUT_S = 600/DEFAULT_DEPLOY_ALIVE_GRACE_S = 90/DEFAULT_DEPLOY_SHUTDOWN_GRACE_S = 30(L57) — Module-level grace constants overridable via--timeout/--deploy-alive-grace/--deploy-shutdown-grace.RunMode = Literal["sim", "benchmark", "deploy"](L61) — Tier selector on eachConfigSpecrow; drives_run_onevs_run_one_deploydispatch.@dataclass(frozen=True) class ConfigSpec(config, rskill, uv_group, run_mode)(L65) — One row in the audit catalogue.uv_groupis one oflibero / metaworld / robocasa / maniskill3 / simpler-env / sim;run_modeis"sim"/"benchmark"/"deploy";rskillis""for deploy rows (env-only — reasoner picks at runtime). Catalogue holds only pairs that actually exist in the tree — scenes without a matching in-tree rSkill are tracked in the scene YAML itself (and intests/unit/test_examples_sim_configs_load.pyfor schema-load coverage), not as audit rows.CATALOGUE: tuple[ConfigSpec, ...](L100) — Explicit (YAML → rSkill → uv group → run_mode) mapping for every config currently in the tree: 13 sim + 7 benchmark + 4 deploy = 24 rows.@dataclass class AuditRow(config, rskill, status, exit_code, wall_s, peak_vram_mib, tail)(L213) — One result.status∈ {pass,pass-compat,fail-oom,fail-asset,fail-sidecar,fail-timeout,fail-other,fail-compat,skipped-opt-dep,skipped-host-setup}._classify(returncode: int, tail: str) -> str(L268) — Map subprocess result to a status by substring-matching stderr against_OOM_PATTERNS/_ASSET_PATTERNS/_SIDECAR_PATTERNS/_OPT_DEP_PATTERNS/_HOST_SETUP_PATTERNS. Exit 139 (MuJoCo/GL atexit SIGSEGV in gym-aloha) is treated aspasswhen no error patterns appear.class _VramSampler(L307) — Backgroundnvidia-smi --query-gpu=memory.usedpoller, 200 ms cadence;peak_mibreported on.stop(). No-op withoutnvidia-smion$PATH._check_compat(spec: ConfigSpec) -> AuditRow(L352) —--check-compatibilitygate: load scene viaopenral_core.load_scene_strict, validate rSkill manifest (sim/benchmark) or assert robot resolves inopenral_cli.deploy_sim._ROBOT_HAL_REGISTRY(deploy). No subprocess, no GPU. Returnspass-compat/fail-compat._build_run_cmd(spec: ConfigSpec) -> list[str](L449) — Build theuv run … openral <sim|benchmark> …argv for sim/benchmark rows. Refactored out of_run_oneso the deploy path can stay focused on lifecycle teardown._run_one_deploy(spec, *, alive_grace_s, shutdown_grace_s, timeout_s) -> AuditRow(L498) — Tier-2 deploy launch viaopenral deploy sim --config <yaml> --no-dashboard:Popenin its own process group, waitalive_grace_s, send SIGINT to the group, waitshutdown_grace_s, escalate to SIGKILL on timeout. Pass criteria: banner seen in stdout AND returncode in{0, -SIGINT, 130, -SIGTERM}._classify_or_fallback(returncode, tail, spec, wall_s, peak_vram) -> AuditRow(L683) — Deploy-mode wrapper around_classifythat defaults tofail-otherwhen no pattern matches (sim path defaults topass)._run_one(spec: ConfigSpec, timeout_s: int) -> AuditRow(L728) — Tier-3 sim/benchmark rollout via_build_run_cmd(spec)withMUJOCO_GL=eglandOPENRAL_SIM_SEQUENTIAL_INIT=1.main(argv) -> int(L875) — CLI entry; flags--timeout/--deploy-alive-grace/--deploy-shutdown-grace/--check-compatibility/--report. Returns 0 on all-pass, 1 if any config failed, 2 on filter mismatch.
tools/select_tests.py
Selective test execution — maps a git diff to the minimal pytest targets that can observe it. Backs just test-changed / the test-selective workflow. See docs/contributing/selective-testing.md.
class SelectionConfig(BaseModel)(L65) — Typed view oftools/test_selection.toml:full_run_globs,ignore_globs,isolate_globs,extra_triggers.class SelectionResult(BaseModel)(L74) —full_run/full_run_reason/affected_packages/targets/isolated_targets(own-process, issue #24) /reasons(per-target rationale).load_config(path) -> SelectionConfig(L97) — Load + validate the TOML config.package_dir_import_names(repo_root) -> dict[str, str](L109) —python/<dir>→ itssrc/openral_*import name.build_dependency_graph(repo_root) -> dict[str, set[str]](L128) — Import-name → directopenraldeps, derived from eachpyproject.toml(never hand-written).transitive_dependents(graph, changed) -> set[str](L152) — Closure of packages that depend on any changed package (includeschanged).map_test_imports(repo_root) -> dict[str, set[str]](L179) — Each top-leveltests/file → theopenral_*packages it imports.select(repo_root, changed_files, config) -> SelectionResult(L260) — Resolve changed paths to pytest targets (blast-radius → full run; else per-package dirs + import-intersecting tests), peelingisolate_globsmatches intoisolated_targets.changed_files_from_git(base, head, repo_root) -> list[str](L364) — Merge-basegit diff --name-only base...head.main(argv=None) -> int(L399) — CLI;--files/--base/--head,--github-outputfor CI step outputs.
tools/audit_tests.py
Test-suite auditor — flags dead / shadowed / duplicate / no-assertion tests; writes docs/contributing/test-audit.md. Read-only; never deletes. Backs just test-audit.
class TestFuncInfo(BaseModel)(L73) — Onetest_*function:path,qualname(class-scoped),markers,has_assertion,is_trivial,body_hash, …class DuplicateGroup(BaseModel)(L88) — Abody_hashshared by ≥2 functions and theirmembers.class AuditReport(BaseModel)(L93) — Inventory (by_tier/by_marker/by_directory) plustrivial/shadowed/no_assertion/duplicate_groups.collect(repo_root) -> list[TestFuncInfo](L212) — Scope-aware AST walk over every test root (same name in two classes is not conflated).build_report(records) -> AuditReport(L245) — Group into the inventory + finding buckets;shadowed= same(path, qualname)redefined (earlier def is dead).render_markdown(report) -> str(L297) — Render the committed report.main(argv=None) -> int(L384) — CLI;--json/--write-report.
python/observability/src/openral_observability/replay/
ADR-0018 F7 — query-time joiner for rosbag2 (mcap) ↔ OTel spans. Backs openral replay + openral record.
bag_reader.py:@dataclass(frozen=True) class BagMessage(topic, log_time_ns, publish_time_ns, trace_id, traceparent, schema_name, payload_summary)(L43) — One mcap record surfaced to the correlator.read_bag(bag_path: str | Path) -> Iterator[BagMessage](L109) — Iterate an.mcapfile or rosbag2 directory; extractstrace_idfromjsonschema-encoded payloads (a packed W3Ctraceparentin thetrace_idfield, OR — for theopenral_msgs/Tickschema — a raw 32-hextrace_id+ raw 16-hexspan_idpair, ISSUE-109) orros2msg-encoded CDR payloads (regex match on the W3Ctraceparentsubstring). Norosbag2_pydep.trace_query.py:class TraceQueryError(RuntimeError)(L17) — Raised on a non-JSON or unreachable dashboard response.@dataclass(frozen=True) class DashboardTraceClient(base_url="http://127.0.0.1:8000", timeout_s=5.0)(L24) —list_traces() -> list[dict]over/api/traces;get_spans(trace_id) -> list[dict]over/api/spans/<id>.correlator.py:@dataclass(frozen=True) class TimelineEntry(kind, ts_ns, trace_id, topic, span_name, attrs, duration_ms)(L26) — One row of the joined timeline;.to_json()returns a plain dict.list_bag_trace_ids(bag_messages) -> list[dict](L67) — Distinct trace_ids in the bag with counts, busiest first.build_timeline(bag_messages, spans, *, trace_id=None) -> list[TimelineEntry](L94) — Pure join. Filters both inputs totrace_id, merges, sorts ascending byts_ns.cli.py:RECORD_PROFILES: dict[str, dict[str, list[str]]](L45) — Slim and full topic + regex presets matching ADR-0018 §F7.build_record_command(*, profile, output_dir, storage="mcap", extra_topics=(), extra_regex=()) -> list[str](L85) — Composeros2 bag recordargv.@dataclass(frozen=True) class ReplayResult(trace_id, bag_trace_ids, timeline, bag_path)(L132) —.to_json()returns a plain dict.run_replay(*, bag_path, trace_id, dashboard_url) -> ReplayResult(L162) — Read a bag, fetch matching spans from the dashboard, return the joined timeline.run_record(*, profile, output_dir, storage="mcap", extra_topics=(), extra_regex=(), dry_run=False) -> tuple[list[str], CompletedProcess | None](L210) — Spawnros2 bag recordin a new process group; forwards SIGINT/SIGTERM received by the parent as SIGINT to the child group so rosbag2 flushesmetadata.yamlcleanly. Waits up to 5 s after the child exits for that file to appear.write_timeline(result: ReplayResult, out_path: Path) -> None(L246) — Persist the timeline JSON.
tools/rskill_publisher.py
Package and publish a local rSkill directory to the HF Hub.
public_visibility_error(manifest, public) -> str | None(L76) — §9 license gate (pure, no network): returns an error string if--publicis requested for a non-commercial-licensed skill (not manifest.is_commercial_use_allowed), elseNone. Letsmainfail fast before any HF call._resolve_token(token_arg) -> str— Prefer CLI arg, fall back to env. (L121)_validate_manifest(skill_dir) -> RSkillManifest(L140)_validate_docs(skill_dir, manifest) -> DocValidationReport— Print + return the README / manifest documentation report via_rskill_doc_validator.validate_rskill_docs. Runs in both dry-run and--publishpaths; the caller decides whether to exit on errors._bump_revision(manifest_path, weights_uri_base, token) -> str— Resolve latest weights commit, patchrskill.yaml. (L176)_ensure_private(api, repo_id) -> None— Abort if the repo is public. (L227)_ensure_public(api, repo_id) -> None— The--publiccounterpart: abort if the (reused) repo is private, so a--publicpublish never lands in a private repo._publish(skill_dir, manifest, token, *, public=False) -> str— Create the HF repo (private unlesspublic) and upload; runs the matching visibility gate (_ensure_public/_ensure_private) aftercreate_repo.main() -> None— Entry point. Sequence: parse args (--publish/--public/--bump-revision/--token) → validate manifest → validate docs →public_visibility_errorgate (exit 1 if--publicon a non-commercial skill) → exit 1 on doc errors → optional--bump-revision→--publish(private unless--public).
tools/rskill_scaffolder.py
Standalone argparse wrapper around openral_cli._rskill_scaffolder.scaffold_rskill.
Mirrors openral rskill new; exists so power users can scaffold without installing the CLI distribution.
_parse_args(argv) -> argparse.Namespace— argparse setup. (L35)main(argv=None) -> int— Entry point; returns a process exit code. (L77)
tools/rldx_sidecar.py
Boot helper for the RLDX-1 inference sidecar (companion to openral_sim.policies.rldx).
Materialises a Python 3.10 venv under _DEFAULT_HOME (~/.cache/openral/rldx-sidecar, override via --home), clones the upstream RLWRLD/RLDX-1 repo, runs uv sync (rldx + transformers + flash-attn + …), optionally adds bitsandbytes for NF4, then writes a wrapper that monkey-patches transformers.AutoModel.from_pretrained to apply NF4 / int8 to the Qwen3-VL-8B backbone (the MSAT diffusion head is left at bf16) and os.execvpes into rldx.eval.run_rldx_server. Required because the rldx package pins requires-python = "~=3.10" and ships a custom architectures=["RLDX"] class not in HF Transformers.
_install_deps(*, source, uv, quantization) -> Path—uv syncin the cloned source tree (createssource/.venvwith rldx + deps), thenuv pip install --python <venv>/bin/python bitsandbytes>=0.43.0whenquantization in {nf4, int8}. Returns the venv path. (L48)_make_wrapper(*, work, source, args) -> Path— Generate<work>/boot_server.py: monkey-patchesAutoModel.from_pretrainedfor the Qwen3-VL backbone with NF4 / int8 / no-op, setssys.argv, and calls intorldx.eval.run_rldx_server. (L82)main() -> int— argparse entry point; flags--model,--port,--quantization {none,nf4,int8},--home. Callsrun_sidecar(..., family="rldx", ...), which stamps the sidecar identity record (so the adapter can verify reuse) and thenos.execvpes into the sidecar venv so SIGINT reaches the server. (L237)
tools/qwen_vlm_sidecar.py + tools/_qwen_vlm_server.py
Boot helper + server for the Qwen3.5-4B scene-VLM sidecar (ADR-0047), companion to openral_runner.backends.gstreamer.qwen_scene_vlm.QwenSceneVlm. The launcher provisions an isolated venv (OPENRAL_QWEN_VLM_SIDECAR_VENV to reuse one) with transformers + bitsandbytes + qwen-vl-utils + pyzmq/msgpack, then os.execvpes into the server. The server answers a ZMQ REQ/REP + msgpack protocol ({"op":"query","image","question"} → {"ok","answer"}); out-of-process for dependency/VRAM isolation (same pattern as rldx_sidecar). Apache-2.0 model.
ensure_venv(home, *, override=None) -> Path(sidecar) — return the sidecar venv python, provisioning + installing pinned deps if absent (sentinel-guarded); honours$OPENRAL_QWEN_VLM_SIDECAR_VENV.main() -> int(sidecar) — argparse (--model,--host,--port,--max-side,--home,--venv); stripsPYTHONPATH/PYTHONHOMEandos.execvpes into_qwen_vlm_server.py._load(model_id) -> (processor, model)/_query(...) -> str/main() -> int(server) — dual-path NF4 load (auto-detect a pre-quantized checkpoint via the embeddedquantization_config→ load 4-bit directly; else quantize-at-load, serial materialization for 8 GB); one scene-question→answer generate via the canonical Qwen-VL recipe (strips the<think>trace); ZMQ REP loop (ping/query/shutdown). Validated live (CLAUDE.md §1.2).
tools/robometer_sidecar.py + tools/_robometer_server.py
Boot helper + stateless scoring server for the Robometer-4B reward-monitor sidecar (ADR-0057), companion to openral_runner.backends.reward.robometer_reward.RobometerReward. The launcher provisions an isolated venv (OPENRAL_ROBOMETER_SIDECAR_VENV to reuse one) by uv pip installing the pinned robometer package (commit a669dffc) then forcing transformers==4.57.1 (the resolver pulls 5.x, which breaks the processor), plus pyzmq/msgpack; sets PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True; strips PYTHONPATH/PYTHONHOME; os.execvpes into the server. The server loads the NF4 RBM once (vanilla AutoModel can't load it — no auto_map; loads via robometer.utils.save.load_model_from_hf) and answers a ZMQ REQ/REP + msgpack protocol ({"op":"score","frames","n","width","height","task","num_bins"} → {"ok","progress","success"}). Out-of-process for dependency/VRAM isolation. robometer is not an OpenRAL-trusted org (pinned, isolated). Validated live: 3.33 GB NF4, progress ramps to 0.88 + success 0.90 at task completion.
ensure_venv(home, *, override=None) -> Path(sidecar) — provision/reuse the sidecar venv (pinned robometer + transformers 4.57.1 + pyzmq/msgpack; sentinel-guarded); honours$OPENRAL_ROBOMETER_SIDECAR_VENV.main() -> int(sidecar) — argparse (--weights,--host,--port,--home,--venv);os.execvpes into_robometer_server.py._quantize_nf4_in_place(root, compute_dtype) -> int/class _Scorer(score(frames_rgb, task, num_bins) -> (progress, success), discrete mode) /main() -> int(server) — inlined NF4 rewrite (theopenral_sim._quantizationrule), load-once + per-clip scoring, BGR→RGB conversion, ZMQ REP loop (ping/score/shutdown).
tools/build_qwen_vlm_nf4_checkpoint.py
Reproducible recipe for the published OpenRAL/rskill-qwen35-4b-nf4 pre-quantized NF4 checkpoint (ADR-0047). Runs in the sidecar venv. main() -> int — argparse (--source, --out); loads the upstream model once (NF4 + serial materialization so the bf16 pass fits 8 GB), save_pretraineds the 4-bit weights + processor, then verifies the checkpoint reloads directly as 4-bit (no bf16 spike) and answers a smoke query. Pre-quantizing lets deployment load the 4-bit weights directly (~3.3 GB) with no loader workaround. Distinct from quantize_rskill.py, which writes an install_prequantized_linears-loaded pack for the in-process lerobot runtime; this writes a transformers-native save_pretrained checkpoint for the isolated VLM sidecar.
tools/fix_libero_config.py
Auto-fix for the stale ~/.libero/config.yaml pitfall.
Detects and repairs $LIBERO_CONFIG_PATH/config.yaml (default ~/.libero/config.yaml) when its absolute paths no longer match the currently-active libero package — the file is written once at first LIBERO import and never refreshed, so switching venv / clone / workspace path leaves it pointing at a directory that no longer exists. Wired into the Justfile as _ensure-libero-config (chained off sim-libero / sim-xvla-libero / sim-pi05-libero). Idempotent.
_expected_config(libero_pkg_dir) -> dict[str, str]— Compute the canonicalassets/bddl_files/benchmark_root/datasets/init_statespayload that LIBERO writes on first import. (L47)_parse_yaml_map(text) -> dict[str, str]— Parse the flatkey: valuemap LIBERO writes — no PyYAML dependency. (L65)_render_yaml(payload) -> str— Render the same flat layout. (L79)_locate_active_libero() -> Path—import liberoand return its package directory; raisesRuntimeErrorwith a clear message when LIBERO is absent (caller treats as no-op). (L84)main() -> int— argparse entry point; flags--dry-run,--verbose. Returns 0 when the config matches or after rewriting. (L110)