Spaces:
Runtime error
Runtime error
| """Tests for ai_percent contract enforcement: assembly tier, verbatim | |
| ratio target, deterministic stitcher, and prompt-stated slider value. | |
| """ | |
| from __future__ import annotations | |
| from app.generator.postprocess import verbatim_overlap_ratio | |
| from app.generator.prompts import ( | |
| build_user_prompt, | |
| top_p_for_ai_involvement, | |
| verbatim_ratio_target, | |
| ) | |
| from app.services.generation import _deterministic_assembly_stitch | |
| class TestTopPCurveMonotonic: | |
| def test_assembly_is_tightest(self) -> None: | |
| assert top_p_for_ai_involvement(0) < top_p_for_ai_involvement(50) | |
| assert top_p_for_ai_involvement(0) < top_p_for_ai_involvement(100) | |
| def test_curve_is_monotonic(self) -> None: | |
| values = [top_p_for_ai_involvement(p) for p in (0, 12, 25, 37, 50, 67, 80, 100)] | |
| for earlier, later in zip(values, values[1:]): | |
| assert earlier <= later, f"top_p decreased from {earlier} to {later}" | |
| def test_assembly_below_one(self) -> None: | |
| assert top_p_for_ai_involvement(0) < 1.0 | |
| assert top_p_for_ai_involvement(5) < 1.0 | |
| class TestVerbatimOverlapRatio: | |
| def test_full_quote_scores_high(self) -> None: | |
| src = "The property is a three-storey semi-detached house in fair condition." | |
| out = "The property is a three-storey semi-detached house in fair condition." | |
| assert verbatim_overlap_ratio(out, [src], n=6) == 1.0 | |
| def test_paraphrase_scores_low(self) -> None: | |
| src = "The property is a three-storey semi-detached house in fair condition." | |
| out = "We observed a moderately maintained three-level semi-detached dwelling." | |
| assert verbatim_overlap_ratio(out, [src], n=6) < 0.2 | |
| def test_empty_inputs_return_zero(self) -> None: | |
| assert verbatim_overlap_ratio("", ["any"], n=6) == 0.0 | |
| assert verbatim_overlap_ratio("any text here please", [], n=6) == 0.0 | |
| assert verbatim_overlap_ratio("short", ["short"], n=6) == 0.0 | |
| def test_partial_quote_proportional(self) -> None: | |
| src = "The roof covering is in fair condition with no significant defects observed." | |
| out = "The roof covering is in fair condition. We also noticed loose tiles near the ridge." | |
| ratio = verbatim_overlap_ratio(out, [src], n=6) | |
| assert 0.0 < ratio < 1.0 | |
| class TestDeterministicStitcher: | |
| BOILERPLATE = ( | |
| "The property comprises a traditionally constructed dwelling of brick and " | |
| "block cavity wall construction beneath a pitched and tiled roof. " | |
| "Inspection was carried out from ground level using a non-invasive visual approach." | |
| ) | |
| def test_includes_source_wording_verbatim(self) -> None: | |
| out = _deterministic_assembly_stitch( | |
| skeleton="[A]: description.", | |
| bullets=["3 Acacia Avenue, postcode SW1A 1AA", "freehold, vacant"], | |
| paragraph_snippets=[self.BOILERPLATE], | |
| document_snippets=None, | |
| hierarchy_section_snippets=None, | |
| survey_level=3, | |
| redact_references=False, | |
| ) | |
| assert "traditionally constructed dwelling" in out | |
| assert "non-invasive visual approach" in out | |
| def test_appends_bullet_notes(self) -> None: | |
| out = _deterministic_assembly_stitch( | |
| skeleton="[A]: description.", | |
| bullets=["3 Acacia Avenue, postcode XX1 1XX", "freehold, vacant"], | |
| paragraph_snippets=[self.BOILERPLATE], | |
| document_snippets=None, | |
| hierarchy_section_snippets=None, | |
| survey_level=3, | |
| redact_references=False, | |
| ) | |
| assert "Site-specific observations" in out | |
| assert "Acacia Avenue" in out | |
| def test_redaction_strips_other_property_facts(self) -> None: | |
| ref_with_facts = ( | |
| "The property at 25 Cunliffe Road, London SW4 8QE is a Victorian terrace. " | |
| "Inspection was carried out from ground level." | |
| ) | |
| out = _deterministic_assembly_stitch( | |
| skeleton="[A]: description.", | |
| bullets=["different property notes"], | |
| paragraph_snippets=[ref_with_facts], | |
| document_snippets=None, | |
| hierarchy_section_snippets=None, | |
| survey_level=3, | |
| redact_references=True, | |
| ) | |
| assert "SW4 8QE" not in out | |
| assert "Cunliffe Road" not in out | |
| assert "Inspection was carried out" in out | |
| def test_returns_empty_when_no_boilerplate(self) -> None: | |
| out = _deterministic_assembly_stitch( | |
| skeleton="[A]: description.", | |
| bullets=["only bullets here"], | |
| paragraph_snippets=None, | |
| document_snippets=None, | |
| hierarchy_section_snippets=None, | |
| survey_level=3, | |
| ) | |
| assert out == "" | |
| def test_falls_back_to_document_snippets(self) -> None: | |
| out = _deterministic_assembly_stitch( | |
| skeleton="[A]: description.", | |
| bullets=["one bullet"], | |
| paragraph_snippets=None, | |
| document_snippets=[self.BOILERPLATE], | |
| hierarchy_section_snippets=None, | |
| survey_level=3, | |
| redact_references=False, | |
| ) | |
| assert "traditionally constructed dwelling" in out | |
| def test_no_synthetic_advice_phrases(self) -> None: | |
| out = _deterministic_assembly_stitch( | |
| skeleton="[A]", | |
| bullets=["bullet text alpha"], | |
| paragraph_snippets=[self.BOILERPLATE], | |
| document_snippets=None, | |
| hierarchy_section_snippets=None, | |
| survey_level=3, | |
| redact_references=False, | |
| ) | |
| synthetic_phrases = [ | |
| "we recommend", | |
| "should be replaced", | |
| "in our professional opinion", | |
| "it is advisable", | |
| ] | |
| for phrase in synthetic_phrases: | |
| assert phrase not in out.lower(), f"synthetic phrase {phrase!r} leaked into stitcher output" | |
| class TestVerbatimRatioTarget: | |
| def test_zero_percent_demands_full_verbatim(self) -> None: | |
| target, floor = verbatim_ratio_target(0) | |
| assert target == 1.0 | |
| assert floor >= 0.9 | |
| def test_full_ai_no_verbatim_floor(self) -> None: | |
| target, floor = verbatim_ratio_target(100) | |
| assert target == 0.0 | |
| assert floor == 0.0 | |
| def test_target_decreases_monotonically(self) -> None: | |
| targets = [verbatim_ratio_target(p)[0] for p in (0, 25, 50, 75, 100)] | |
| for earlier, later in zip(targets, targets[1:]): | |
| assert earlier >= later | |
| def test_floor_never_above_target(self) -> None: | |
| for p in (10, 25, 50, 75): | |
| target, floor = verbatim_ratio_target(p) | |
| assert 0.0 <= floor <= target | |
| class TestPromptStatesSliderRatio: | |
| """The user-visible prompt MUST state the slider's target ratio so the | |
| LLM has a concrete number to hit (and so we can audit drift).""" | |
| def test_assembly_prompt_quotes_slider_value(self) -> None: | |
| prompt = build_user_prompt( | |
| skeleton="A", | |
| bullets=["alpha"], | |
| paragraph_snippets=["The property comprises a traditionally constructed dwelling."], | |
| ai_percent=0, | |
| survey_level=3, | |
| reference_only_context=True, | |
| ) | |
| assert "0%" in prompt | |
| assert "verbatim" in prompt.lower() | |
| def test_low_tier_prompt_quotes_slider_value(self) -> None: | |
| prompt = build_user_prompt( | |
| skeleton="A", | |
| bullets=["alpha"], | |
| paragraph_snippets=["Inspection was carried out from ground level."], | |
| ai_percent=25, | |
| survey_level=3, | |
| reference_only_context=True, | |
| ) | |
| assert "25%" in prompt | |
| assert "75%" in prompt | |
| def test_mid_tier_prompt_quotes_slider_value(self) -> None: | |
| prompt = build_user_prompt( | |
| skeleton="A", | |
| bullets=["alpha"], | |
| paragraph_snippets=["Inspection was carried out from ground level."], | |
| ai_percent=50, | |
| survey_level=3, | |
| reference_only_context=True, | |
| ) | |
| assert "50%" in prompt | |