diff --git a/src/inspect_robots/approver.py b/src/inspect_robots/approver.py index 6aaa51b9..90bbebf1 100644 --- a/src/inspect_robots/approver.py +++ b/src/inspect_robots/approver.py @@ -252,14 +252,18 @@ def rewind_reference(store: dict[str, Any], pose: npt.NDArray[np.float64]) -> No the limiter measures subsequent deltas from the pose that actually ran. If the limiter has not established a reference, this is a no-op. """ - if _LAST_APPROVED_KEY in store: + if _LAST_APPROVED_KEY in store and bool(np.all(np.isfinite(pose))): store[_LAST_APPROVED_KEY] = pose.copy() def review(self, action: Action, store: dict[str, Any]) -> Action: """Limit per-step change, retaining absolute-mode history in trial state.""" data = np.asarray(action.data, dtype=np.float64) - if bool(np.isnan(data).any()): - raise SafetyAbort("DeltaLimitApprover: action contains NaN; refusing to pass it on") + if not bool(np.all(np.isfinite(data))): + raise SafetyAbort( + "DeltaLimitApprover: action contains NaN or non-finite values; " + "refusing to pass it on" + ) + if self._absolute: reference = store.get(_LAST_APPROVED_KEY) if reference is None: diff --git a/tests/test_approvers.py b/tests/test_approvers.py index 8741e491..b4a20c58 100644 --- a/tests/test_approvers.py +++ b/tests/test_approvers.py @@ -208,6 +208,16 @@ def test_rewind_reference_uses_the_limiter_store_key() -> None: assert np.array_equal(reference, np.array([0.3, 0.4])) +def test_delta_limit_approver_rejects_inf() -> None: + approver = DeltaLimitApprover(_abs_space(), max_delta=0.1) + store: dict[str, object] = {} + with pytest.raises(SafetyAbort, match="non-finite"): + approver.review(Action(data=np.array([float("inf"), 0.0])), store) + + with pytest.raises(SafetyAbort, match="non-finite"): + approver.review(Action(data=np.array([0.0, float("-inf")])), store) + + def test_substitution_rewinds_the_next_delta_reference() -> None: held = Action(data=np.array([0.0, 0.0]))