diff --git a/src/lerobot/policies/pi052/modeling_pi052.py b/src/lerobot/policies/pi052/modeling_pi052.py index 24aa3834f..14cb233b6 100644 --- a/src/lerobot/policies/pi052/modeling_pi052.py +++ b/src/lerobot/policies/pi052/modeling_pi052.py @@ -1751,8 +1751,6 @@ class PI052Policy(PreTrainedPolicy): return out def _generate_low_level_subtask(self, obs_i: dict[str, Tensor], task: str, i: int) -> str: - from lerobot.runtime.adapter import looks_like_gibberish as _looks_like_gibberish # noqa: PLC0415 - from .inference.pi052_adapter import _generate_with_policy # noqa: PLC0415 msg = "" @@ -1767,7 +1765,7 @@ class PI052Policy(PreTrainedPolicy): self.last_subtasks_raw[i] = msg or "" # Feed the generated subtask verbatim, matching low-level training. - if msg and not _looks_like_gibberish(msg): + if msg: subtask = " ".join(msg.strip().split()) self._last_good_subtasks[i] = subtask self.last_subtasks[i] = subtask @@ -1779,8 +1777,6 @@ class PI052Policy(PreTrainedPolicy): debug = getattr(self, "_last_select_message_debug", "") or "" if not task: reason = "No task string was available in the batch." - elif msg: - reason = f"Rejected generated subtask: {msg!r}" else: reason = f"Empty generated subtask. {debug}".strip() if self._last_good_subtasks[i]: diff --git a/src/lerobot/runtime/adapter.py b/src/lerobot/runtime/adapter.py index 1c499a594..e94a19a4a 100644 --- a/src/lerobot/runtime/adapter.py +++ b/src/lerobot/runtime/adapter.py @@ -43,11 +43,10 @@ class GenerationConfig: @dataclass class LanguageDiagnostics: - """Runtime-panel rejection and repeat counters keyed by text kind.""" + """Runtime-panel generation counters keyed by text kind.""" last_raw: dict[str, str] = field(default_factory=dict) empty: dict[str, int] = field(default_factory=dict) - gibberish: dict[str, int] = field(default_factory=dict) repeat: int = 0 def _bump(self, table: dict[str, int], kind: str) -> int: @@ -96,9 +95,9 @@ class BaseLanguageAdapter(ABC): state.set_context("plan", plan, label="plan") def plan_from_text(self, text: str) -> str: - """Strip ```` speech markers and reject gibberish plans.""" + """Strip ```` speech markers from a generated plan.""" plan, _speech = split_plan_and_say(text) - return "" if looks_like_gibberish(plan) else plan + return plan def _regenerate_context(self, observation: dict[str, Any] | None, state: RuntimeState) -> None: """Default hierarchy: regenerate the subtask, then memory when it changes. @@ -127,7 +126,7 @@ class BaseLanguageAdapter(ABC): def _generate_filtered( self, kind: str, observation: dict[str, Any] | None, state: RuntimeState ) -> str | None: - """Generate one ``kind``, record diagnostics, drop empty / gibberish output.""" + """Generate one ``kind``, record diagnostics, and drop empty output.""" text = self.generate_text(kind, observation, state) self.diag.last_raw[kind] = text or "" if not text: @@ -135,11 +134,6 @@ class BaseLanguageAdapter(ABC): if count == 1 or count % 5 == 0: state.log(f" [info] {kind} gen returned empty (x{count})") return None - if looks_like_gibberish(text): - count = self.diag._bump(self.diag.gibberish, kind) - if count == 1 or count % 30 == 0: - state.log(f" [info] {kind} gen rejected (gibberish x{count}): {text[:60]!r}") - return None return text @@ -159,29 +153,6 @@ class DirectTaskPolicyAdapter(BaseLanguageAdapter): return "" -def looks_like_gibberish(text: str) -> bool: - """Heuristic filter for malformed / collapsed LM-head output.""" - if not text or not text.strip(): - return True - stripped = text.strip() - alpha = sum(1 for c in stripped if c.isalpha()) - if alpha < max(3, len(stripped) // 8): - return True - if stripped.startswith('":') and stripped.count('"') > stripped.count(" "): - return True - if len(set(stripped)) <= 2 and len(stripped) > 4: - return True - cleaned = stripped.replace("\n", " ").replace(":", " ") - for marker in ("Assistant", "User", "Ass "): - if marker in cleaned and len(cleaned.split()) < 4: - return True - tokens = [t for t in cleaned.split() if any(c.isalpha() for c in t)] - unique_alpha = {t.lower() for t in tokens} - if len(unique_alpha) < 3 and len(stripped) < 80: - return True - return len(tokens) >= 8 and len(unique_alpha) <= max(3, len(tokens) // 10) - - def split_plan_and_say(text: str) -> tuple[str, str]: """Split ``plan speech`` into ``(plan, speech)``.""" if not text: diff --git a/src/lerobot/runtime/cli.py b/src/lerobot/runtime/cli.py index eaab65eda..dd6c8c307 100644 --- a/src/lerobot/runtime/cli.py +++ b/src/lerobot/runtime/cli.py @@ -660,24 +660,19 @@ def _make_state_panel_renderer( dispatched = int(st.get("actions_dispatched") or 0) console.print(f" [dim]queued actions: {queue_len} dispatched: {dispatched}[/]") - # Surface repeated or rejected generations as overfitting diagnostics. + # Surface repeated or empty generations as overfitting diagnostics. diag = getattr(runtime.policy_adapter, "diag", None) if diag is not None: raw_subtask = diag.last_raw.get("subtask") sub_rep = int(diag.repeat) - sub_gib = int(diag.gibberish.get("subtask", 0)) sub_empty = int(diag.empty.get("subtask", 0)) - if raw_subtask is not None or sub_rep or sub_gib or sub_empty: + if raw_subtask is not None or sub_rep or sub_empty: raw_display = (raw_subtask or "(empty)")[:80] - color = "yellow" if (sub_rep >= 3 or sub_gib >= 3 or sub_empty >= 3) else "dim" + color = "yellow" if (sub_rep >= 3 or sub_empty >= 3) else "dim" console.print( - f" [{color}]subtask diag repeat:{sub_rep} " - f"gibberish:{sub_gib} empty:{sub_empty} " + f" [{color}]subtask diag repeat:{sub_rep} empty:{sub_empty} " f"last_raw: {raw_display!r}[/]" ) - mem_gib = int(diag.gibberish.get("memory", 0)) - if mem_gib: - console.print(f" [dim]gen rejects memory:{mem_gib}[/]") console.rule(style="cyan") # Show recent generation warnings and speech oldest-first. if scrollback: diff --git a/tests/runtime/test_adapter.py b/tests/runtime/test_adapter.py index fb1fbc469..470ef13ab 100644 --- a/tests/runtime/test_adapter.py +++ b/tests/runtime/test_adapter.py @@ -17,7 +17,6 @@ from lerobot.runtime.adapter import ( BaseLanguageAdapter, DirectTaskPolicyAdapter, GenerationConfig, - looks_like_gibberish, ) @@ -49,15 +48,15 @@ def test_cascade_sets_subtask_then_memory(): assert adapter.calls == ["subtask", "memory"] -def test_gibberish_subtask_is_rejected_and_counted(): - adapter = ScriptedAdapter({"subtask": [":::: ::"], "memory": ["should not run"]}) +def test_nonempty_generation_is_used_verbatim(): + adapter = ScriptedAdapter({"subtask": [":::: ::"], "memory": ["memory"]}) state = RuntimeState(task="clean") adapter.update_language_state(None, state) - assert "subtask" not in state.language_context - assert adapter.diag.gibberish.get("subtask") == 1 - assert adapter.calls == ["subtask"] # memory never generated when subtask is rejected + assert state.language_context["subtask"] == ":::: ::" + assert state.language_context["memory"] == "memory" + assert adapter.calls == ["subtask", "memory"] def test_throttle_regenerates_every_n_chunks(): @@ -87,12 +86,6 @@ def test_handle_interjection_sets_plan_and_strips_say(): assert state.language_context["plan"] == "turn to the left now" -def test_looks_like_gibberish_basic(): - assert looks_like_gibberish("") - assert looks_like_gibberish(":::: ::") - assert not looks_like_gibberish("pick up the red cube") - - def test_direct_task_adapter_delegates_action_chunk(): class Policy: def predict_action_chunk(self, observation):