From d7f069b7c58af9311fd913e3709ca20279bef753 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 1 Jul 2026 13:05:47 +0000 Subject: [PATCH] =?UTF-8?q?Deterministically=20guard=20structured=20biomas?= =?UTF-8?q?s=20and=20immersion=20water-heating=20descriptions=20?= =?UTF-8?q?=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../property_overrides/water_heating_guard.py | 9 +++ tests/domain/epc/test_water_heating_guard.py | 80 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 domain/epc/property_overrides/water_heating_guard.py create mode 100644 tests/domain/epc/test_water_heating_guard.py diff --git a/domain/epc/property_overrides/water_heating_guard.py b/domain/epc/property_overrides/water_heating_guard.py new file mode 100644 index 000000000..4b447ff6e --- /dev/null +++ b/domain/epc/property_overrides/water_heating_guard.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from typing import Optional + +from domain.epc.property_overrides.water_heating_type import WaterHeatingType + + +def water_heating_guard(description: str) -> Optional[WaterHeatingType]: + raise NotImplementedError diff --git a/tests/domain/epc/test_water_heating_guard.py b/tests/domain/epc/test_water_heating_guard.py new file mode 100644 index 000000000..227ed3f4e --- /dev/null +++ b/tests/domain/epc/test_water_heating_guard.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +import pytest + +from domain.epc.property_overrides.water_heating_guard import water_heating_guard +from domain.epc.property_overrides.water_heating_type import WaterHeatingType + + +@pytest.mark.parametrize( + ("description", "expected"), + [ + # The biomass/wood family the LLM funnelled into "house coal" (#1376). + ( + "From main heating system: Wood Logs", + WaterHeatingType.FROM_MAIN_WOOD_LOGS, + ), + ( + "From main heating system: Dual Fuel: Mineral and Wood", + WaterHeatingType.FROM_MAIN_DUAL_FUEL_MINERAL_WOOD, + ), + # "Hot-water only community scheme" flattens to "from main", per the + # existing convention (community mains-gas already resolves that way). + ( + "From main heating system: Biomass (Community)", + WaterHeatingType.FROM_MAIN_BIOMASS_COMMUNITY, + ), + ( + "Hot-water only community scheme - boilers: Biomass (Community)", + WaterHeatingType.FROM_MAIN_BIOMASS_COMMUNITY, + ), + ( + "Hot-water only community scheme - boilers: Heat from Boilers " + "using Biodiesel from any source (Community)", + WaterHeatingType.FROM_MAIN_BIODIESEL_COMMUNITY, + ), + # "No Heating or Hot Water" under "electric immersion assumed" is the + # assessment's own immersion assumption — honour it (ADR-0043). + ( + "No hot water system present - electric immersion assumed: " + "No Heating or Hot Water", + WaterHeatingType.ELECTRIC_IMMERSION, + ), + # A genuinely coal DHW stays house coal. + ( + "From main heating system: House Coal", + WaterHeatingType.FROM_MAIN_HOUSE_COAL, + ), + ], +) +def test_guard_resolves_the_structured_biomass_and_immersion_descriptions( + description: str, expected: WaterHeatingType +) -> None: + # Act + result = water_heating_guard(description) + + # Assert + assert result is expected + + +@pytest.mark.parametrize( + "description", + [ + # Fuels the LLM already classifies correctly — the guard must not claim + # them (it only rescues the previously-misclassified structured cases). + "From main heating system: Mains Gas", + "Electric immersion: Electricity", + "From main heating system: Oil", + # A varied / unstructured phrasing is the LLM's job. + "hot water from the wood burner", + "", + ], +) +def test_guard_defers_recognised_and_unstructured_descriptions_to_the_llm( + description: str, +) -> None: + # Act + result = water_heating_guard(description) + + # Assert + assert result is None