Spaces:
Sleeping
Sleeping
File size: 8,019 Bytes
34f3bc9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | """Central registry of every ``LABVLA_*`` environment flag.
Why: dozens of escape-hatch/tuning flags accumulated across modules with no
single place that says what exists, what the default is, or who reads it. A
single registry prevents silent launcher divergence on the same flag.
Contract:
* ``get(name)`` returns the raw string value with the REGISTERED default —
call sites keep their own comparison idiom (``== "1"`` / ``!= "0"`` /
``int(...)``) so migration is semantics-preserving by construction.
* Entries with ``default=None`` are DOCUMENT-ONLY ("site-managed"): the
reading module still owns parsing/default (e.g. storage_retry's five
numeric knobs, deployment's path/secret). They are listed so the registry
is the one complete inventory.
* ``validate_environment()`` warns about set-but-unregistered ``LABVLA_*``
vars — typos like ``LABVLA_ALOW_TRUNCATE`` used to disappear silently.
"""
from __future__ import annotations
import logging
import os
from dataclasses import dataclass
logger = logging.getLogger(__name__)
@dataclass(frozen=True)
class _Flag:
default: str | None # None => document-only, site keeps its own default
doc: str
consumers: str # module(s) that read it
FLAGS: dict[str, _Flag] = {
# ---- data-pipeline escape hatches (default fail-loud) -----------------
"LABVLA_ALLOW_TRUNCATE": _Flag(
"0", "truncate overwide state/action columns instead of raising "
"(adapters _pad_row + v2/v3 stats readers + scan-cache key). "
"All production launchers set 0 (fail-loud); the historical 1-vs-0 "
"launcher split was an incident, and there is NO safe repo exception "
"(M47). One-off diagnostics only.",
"adapters/lerobot_base, adapters/lerobot_v21, adapters/lerobot_v30, "
"adapters/_scan_cache, data_process/stats/v2, data_process/stats/v3"),
"LABVLA_ALLOW_TOKENIZED_TASK_COERCION": _Flag(
None, "coerce tokenized (list<int>) v3 task cells to str() instead of "
"raising — inspection-only runs.", "adapters/lerobot_v30"),
"LABVLA_DATA_SKIP_CONTRACT_ERRORS": _Flag(
"0", "let SkipBadSamplesDataset resample on deterministic "
"schema-contract errors (KeyError/ValueError/...) instead of raising "
"— legacy behavior; silently reweights data.", "scripts/utils/dataloader"),
"LABVLA_ALLOW_SCHEMA_OVERRIDE": _Flag(
"0", "allow re-registering a schema name with DIFFERENT content "
"(import-order dependent identity; M66 default fail-loud).",
"schema/registry"),
"LABVLA_ALLOW_MISSING_STATE_SUBKEYS": _Flag(
"0", "let DiscretizeStateTransformFn warn-and-skip samples missing a "
"declared state sub-key instead of raising (M36/M109: mixed prompt "
"contract).", "transforms/state_discretize"),
"LABVLA_ALLOW_Q0199_FALLBACK": _Flag(
"0", "fall back to mean_std/min_max when q01_q99 is requested but "
"stats lack quantiles (legacy warn-only path; breaks gripper "
"open/close alignment).", "transforms/core"),
"LABVLA_ALLOW_MISSING_NORM_STATS": _Flag(
"0", "let hydrate proceed when a normalized key has no stats "
"(silently-unnormalized training).", "transforms/core"),
"LABVLA_ALLOW_UNPATCHED_SNAP_STATS": _Flag(
"0", "skip the snap-gripper stats-canonicalization guard.",
"transforms/core"),
"LABVLA_ALLOW_STATS_INVALIDATED": _Flag(
None, "train despite cleanup-invalidated stats.", "scripts/utils/dataset_build"),
"LABVLA_ALLOW_HETEROGENEOUS_ACTION_DIMS": _Flag(
None, "bypass the multi-repo posttrain action-dim guard.", "scripts/train"),
"LABVLA_ALLOW_HETEROGENEOUS_GRIPPER": _Flag(
None, "bypass the gripper-layout guard under gripper_loss_weight!=1.",
"scripts/train"),
"LABVLA_ALLOW_HETEROGENEOUS_GRIPPER_SEMANTIC": _Flag(
None, "demote the cross-repo gripper-semantic conflict to a warning.",
"scripts/utils/dataset_helpers"),
"LABVLA_FAST_ACTION_NON_CHUNK_SKIP": _Flag(
"0", "legacy silent-skip when the action reaching FAST is not a (T,D) "
"chunk.", "transforms/fast_action"),
# ---- caches / performance --------------------------------------------
"LABVLA_SCAN_CACHE": _Flag(
"1", "0 disables the on-disk adapter scan cache.", "adapters/_scan_cache"),
"LABVLA_VIDEO_CACHE_MAX": _Flag(
"128", "process-wide PyAV container LRU size.", "adapters/lerobot_base"),
"LABVLA_V21_VALIDATE_PER_FILE": _Flag(
"1", "0 trusts one sampled parquet per chunk instead of per-file scan.",
"adapters/lerobot_v21"),
"LABVLA_WORKER_TRIM_EVERY": _Flag(
None, "malloc_trim cadence in DataLoader workers.", "scripts/utils/dataset_helpers"),
"LABVLA_DATA_SKIP_LOG_EVERY": _Flag(
None, "rate limit for bad-sample skip warnings.", "scripts/utils/dataloader"),
# ---- assets / paths ----------------------------------------------------
"LABVLA_FAST_TOKENIZER_PATH": _Flag(
None, "library-level default FAST asset path override.", "transforms/fast_action"),
"LABVLA_SCHEMA_PATH": _Flag(
None, "extra schema dirs (PYTHONPATH-style) for the registry autoload.",
"schema/registry"),
# ---- VQA ----------------------------------------------------------------
"LABVLA_VQA_SKIP_BAD_RECORDS": _Flag(
None, "skip unreadable VQA records instead of raising.",
"dataset/adapters/robointer_vqa_adapter"),
"LABVLA_VQA_SKIP_MAX_ATTEMPTS": _Flag(
None, "'auto' or int retry budget for VQA bad-record skips.",
"dataset/adapters/robointer_vqa_adapter"),
# ---- storage retry (site-managed numeric parsing) ----------------------
"LABVLA_STORAGE_RETRY_ENABLE": _Flag(None, "enable storage retry wrapper.", "utils/storage_retry"),
"LABVLA_STORAGE_RETRY_TOTAL_SECONDS": _Flag(None, "total retry budget.", "utils/storage_retry"),
"LABVLA_STORAGE_RETRY_NOT_FOUND_SECONDS": _Flag(None, "budget for FileNotFound.", "utils/storage_retry"),
"LABVLA_STORAGE_RETRY_INITIAL_SLEEP": _Flag(None, "initial backoff.", "utils/storage_retry"),
"LABVLA_STORAGE_RETRY_MAX_SLEEP": _Flag(None, "max backoff.", "utils/storage_retry"),
# ---- deployment ---------------------------------------------------------
"LABVLA_ROOT": _Flag(None, "repo root override for the serve entrypoint sys.path bootstrap (src. imports resolve from the repo root).",
"deployment/serve_labvla"),
"LABVLA_WS_AUTH_TOKEN": _Flag(None, "websocket auth secret.", "deployment/serve_labvla"),
# Shell-only (never read by Python): node89 dispatcher passes the rg-killer
# daemon its pidfile path through the environment. Registered so
# validate_environment() does not flag it as a typo on dispatch nodes.
"LABVLA_RG_KILLER_PIDFILE": _Flag(None, "rg-killer daemon pidfile (shell-only).",
"launch/_rg_killer.sh, launch/*node89*.sh"),
"LABVLA_DEPLOY_ALLOW_PARTIAL_LOAD": _Flag(
None, "tolerate missing keys when loading deploy weights.", "deployment/serve_labvla"),
}
def get(name: str) -> str | None:
"""Raw string value of a REGISTERED flag, with the registered default.
Raises ``KeyError`` for unregistered names — adding a flag requires adding
a registry entry (that is the point).
"""
flag = FLAGS[name]
return os.environ.get(name, flag.default)
def validate_environment(log: logging.Logger | None = None) -> list[str]:
"""Warn about set-but-unregistered LABVLA_* env vars (likely typos)."""
log = log or logger
unknown = sorted(
k for k in os.environ if k.startswith("LABVLA_") and k not in FLAGS
)
if unknown:
log.warning(
"[env-flags] unrecognized LABVLA_* environment variable(s) set: %s "
"— not read by any registered flag (typo? see utils/env_flags.py).",
unknown,
)
return unknown
|