From 82a52940157b6b2f35efb28e83f5acb74b1a87b4 Mon Sep 17 00:00:00 2001 From: ABrandes <79119643+AMBRA7592@users.noreply.github.com> Date: Sun, 19 Jul 2026 06:16:58 +0200 Subject: [PATCH] Adapters: normalize MHS float categories --- adapters/mhs.py | 12 +++++++++++- test_claims.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/adapters/mhs.py b/adapters/mhs.py index 4073a90..a4ab4a2 100644 --- a/adapters/mhs.py +++ b/adapters/mhs.py @@ -7,6 +7,7 @@ """ import json +import math from collections import Counter, OrderedDict @@ -68,7 +69,16 @@ def _label(value, index): raise MHSInputError( "record {} hatespeech must be null or one of 0, 1, 2".format(index) ) - normalized = str(value) + if isinstance(value, float): + if not math.isfinite(value) or not value.is_integer(): + raise MHSInputError( + "record {} hatespeech must be null or one of 0, 1, 2".format( + index + ) + ) + normalized = str(int(value)) + else: + normalized = str(value) if normalized not in LABELS: raise MHSInputError( "record {} hatespeech must be null or one of 0, 1, 2".format(index) diff --git a/test_claims.py b/test_claims.py index 399c2a4..378ed14 100644 --- a/test_claims.py +++ b/test_claims.py @@ -1411,6 +1411,42 @@ def test_frozen_eligibility_cohorts_primary_filter_and_halt(self): self.assertEqual(halted["counts"]["primary_items"], 49) self.assertIn("below the frozen minimum 50", halted["halt_reason"]) + def test_label_normalizes_only_finite_integral_parquet_floats(self): + self.assertIsNone(mhs_adapter._label(None, 1)) + for value, expected in ( + (0.0, "0"), + (1.0, "1"), + (2.0, "2"), + (0, "0"), + (1, "1"), + (2, "2"), + ("0", "0"), + ("1", "1"), + ("2", "2"), + ): + self.assertEqual(mhs_adapter._label(value, 1), expected) + + for value in ( + 0.5, + 3.0, + -1.0, + math.nan, + math.inf, + -math.inf, + True, + False, + 3, + -1, + "0.0", + "3", + "-1", + ): + with self.subTest(value=value): + with self.assertRaisesRegex( + mhs_adapter.MHSInputError, "one of 0, 1, 2" + ): + mhs_adapter._label(value, 1) + def test_synthetic_tool_wiring_and_frozen_metric_results(self): with tempfile.TemporaryDirectory(prefix="groundless-mhs-") as temp: temp_path = Path(temp)