InsuranceBot / tests /test_eligibility_ranking.py
rohitsar567's picture
data+scoring: verbatim-source all policy_facts, recalibrate scorecard, fix recommendation
7081aaa
Raw
History Blame Contribute Delete
10.7 kB
"""KI-278 (2026-05-16) — eligibility filtering + profile-fit ranking.
Pins the bug reported for the failing session:
USER PROFILE: 29, metro, income 25L+, FIRST policy (existing_cover_inr=0),
no pre-existing conditions, wants 15L sum insured, prefers ZERO co-pay.
WHAT WAS WRONGLY RECOMMENDED:
1. Royal Sundaram "Multiplier" — 20% co-payment (user wants 0%)
2. Royal Sundaram "Advanced Top Up" — super-top-up; needs a base policy
the first-time buyer does NOT have
3. generally B/C-grade plans whose metrics contradict stated needs
Two logic defects, both fixed in backend/retrieval_filters.py:
(a) ELIGIBILITY: top-up / super-top-up plans must be excluded when the
user has no existing base cover (existing_cover_inr falsy / first buy).
(b) PROFILE-FIT RANKING: high co-pay plans must not survive an explicit
zero-copay preference; the SI floor (15L) must gate; better-fit plans
must rank above worse-fit ones.
Run:
.venv/bin/python -m unittest tests.test_eligibility_ranking -v
"""
from __future__ import annotations
import unittest
from backend.retrieval_filters import (
apply_profile_filter,
apply_eligibility_filter,
rank_by_profile_fit,
filter_pipeline,
)
# ---------------------------------------------------------------------------
# The exact failing-session profile.
# ---------------------------------------------------------------------------
FAILING_PROFILE = {
"age": 29,
"location_tier": "metro",
"income_band": "25L+",
"primary_goal": "first_buy",
"existing_cover_inr": 0, # FIRST policy — no base cover
"health_conditions": ["none"],
"desired_sum_insured_inr": 1_500_000, # ₹15 lakh
"copay_pct": 0, # explicit ZERO co-pay preference
"dependents": "self",
}
def _chunk(
policy_id: str,
policy_name: str,
*,
score: float = 0.5,
policy_type: str | None = None,
deductible_amount: int | None = None,
copay_pct: int | None = None,
sum_insured_options: list[int] | None = None,
grade: str | None = None,
overall_score: int | None = None,
doc_type: str = "policy",
) -> dict:
"""Build a chunk dict shaped like brain_tools.retrieve_policies output
AFTER the new policy_facts enrichment step."""
return {
"policy_id": policy_id,
"policy_name": policy_name,
"insurer_slug": policy_id.split("__")[0],
"doc_type": doc_type,
"score": score,
"chunk_text": "",
# Enriched structured facts (added by brain_tools before filtering):
"policy_type_indemnity_or_fixed": policy_type,
"deductible_amount": deductible_amount,
"co_payment_pct": copay_pct,
"sum_insured_options": sum_insured_options,
"_grade": grade,
"_overall_score": overall_score,
}
# Realistic catalog slice mirroring the failing session.
MULTIPLIER = _chunk(
"royal-sundaram__multiplier",
"Multiplier Health Insurance Plan",
score=0.71,
policy_type="family_floater",
deductible_amount=None,
copay_pct=20,
sum_insured_options=[500000, 1000000, 1500000, 2000000, 2500000],
grade="B",
overall_score=72,
)
ADVANCED_TOP_UP = _chunk(
"royal-sundaram__advanced-top-up",
"Advanced Top Up Health Insurance Plan",
score=0.69,
policy_type="super_top_up",
deductible_amount=500000,
copay_pct=0,
sum_insured_options=[1000000, 1500000, 2000000],
grade="B",
overall_score=70,
)
GOOD_FIT_A = _chunk(
"niva-bupa__reassure-2",
"ReAssure 2.0",
score=0.62,
policy_type="indemnity",
deductible_amount=None,
copay_pct=0,
sum_insured_options=[1000000, 1500000, 2000000, 5000000],
grade="A",
overall_score=88,
)
GOOD_FIT_B = _chunk(
"hdfc-ergo__optima-secure",
"Optima Secure",
score=0.58,
policy_type="indemnity",
deductible_amount=None,
copay_pct=0,
sum_insured_options=[500000, 1000000, 1500000, 2000000],
grade="A",
overall_score=85,
)
LOW_SI = _chunk(
"star-health__medi-classic",
"Medi Classic",
score=0.66,
policy_type="indemnity",
deductible_amount=None,
copay_pct=0,
sum_insured_options=[200000, 300000, 500000], # cannot offer 15L
grade="C",
overall_score=60,
)
class TestEligibilityFilter(unittest.TestCase):
"""Defect (a) — top-up / super-top-up exclusion for first-time buyers."""
def test_super_top_up_dropped_when_no_base_cover(self):
kept = apply_eligibility_filter(
[ADVANCED_TOP_UP, GOOD_FIT_A], FAILING_PROFILE
)
ids = {c["policy_id"] for c in kept}
self.assertNotIn(
"royal-sundaram__advanced-top-up", ids,
"Super-top-up must be excluded for a first-time buyer with no "
"existing base cover.",
)
self.assertIn("niva-bupa__reassure-2", ids)
def test_top_up_dropped_by_name_when_facts_missing(self):
# No structured policy_type, but the NAME says "Top Up" / "Super Top Up".
top_up_by_name = _chunk(
"sbi-general__super-top-up",
"SBI Super Top-up Health Insurance",
policy_type=None,
deductible_amount=None,
)
kept = apply_eligibility_filter([top_up_by_name], FAILING_PROFILE)
self.assertEqual(
kept, [],
"A plan named 'Super Top-up' must be excluded for a no-base-cover "
"user even when structured policy_type is missing.",
)
def test_top_up_dropped_by_deductible_signal(self):
# No policy_type, name doesn't say top-up, but it carries a large
# aggregate deductible — that IS the base-cover requirement.
deductible_only = _chunk(
"acko__platinum-super",
"Platinum Plus Plan",
policy_type=None,
deductible_amount=500000,
)
kept = apply_eligibility_filter([deductible_only], FAILING_PROFILE)
self.assertEqual(
kept, [],
"A plan with a ₹5L aggregate deductible is a top-up in disguise "
"and must be excluded for a no-base-cover user.",
)
def test_top_up_kept_when_user_has_base_cover(self):
has_base = dict(FAILING_PROFILE, existing_cover_inr=500000)
kept = apply_eligibility_filter([ADVANCED_TOP_UP], has_base)
self.assertEqual(
len(kept), 1,
"Top-up IS appropriate when the user already has base cover.",
)
def test_si_floor_drops_plans_that_cannot_offer_requested_si(self):
kept = apply_eligibility_filter([LOW_SI, GOOD_FIT_A], FAILING_PROFILE)
ids = {c["policy_id"] for c in kept}
self.assertNotIn(
"star-health__medi-classic", ids,
"A plan whose max SI is ₹5L cannot satisfy a ₹15L requirement.",
)
self.assertIn("niva-bupa__reassure-2", ids)
def test_zero_copay_preference_drops_high_copay_plan(self):
kept = apply_eligibility_filter([MULTIPLIER, GOOD_FIT_A], FAILING_PROFILE)
ids = {c["policy_id"] for c in kept}
self.assertNotIn(
"royal-sundaram__multiplier", ids,
"A 20% co-pay plan must be excluded when the user explicitly "
"wants ZERO co-pay and can afford full cover.",
)
def test_non_policy_chunks_never_dropped(self):
reg = _chunk("irdai__circular", "IRDAI Master Circular",
doc_type="regulatory", policy_type="super_top_up",
deductible_amount=500000)
kept = apply_eligibility_filter([reg], FAILING_PROFILE)
self.assertEqual(len(kept), 1,
"Regulatory/review/profile chunks are never policies.")
class TestProfileFitRanking(unittest.TestCase):
"""Defect (b) — better-fit plans must outrank worse-fit ones."""
def test_a_grade_zero_copay_ranks_above_b_grade(self):
ranked = rank_by_profile_fit(
[MULTIPLIER, GOOD_FIT_B, GOOD_FIT_A], FAILING_PROFILE
)
order = [c["policy_id"] for c in ranked]
self.assertLess(
order.index("niva-bupa__reassure-2"),
order.index("royal-sundaram__multiplier"),
"A-grade zero-copay plan must rank above a B-grade 20%-copay plan "
"even though the B-grade plan had higher raw cosine.",
)
def test_ranking_stable_for_equally_good_plans(self):
ranked = rank_by_profile_fit([GOOD_FIT_A, GOOD_FIT_B], FAILING_PROFILE)
self.assertEqual(len(ranked), 2)
class TestFilterPipelineIntegration(unittest.TestCase):
"""End-to-end: the exact failing catalog through filter_pipeline must
NOT surface Multiplier or Advanced Top Up, and the top result must be a
well-fitting A-grade plan."""
def test_failing_session_catalog(self):
catalog = [
MULTIPLIER, ADVANCED_TOP_UP, LOW_SI, GOOD_FIT_A, GOOD_FIT_B,
]
filtered, guard = filter_pipeline(
catalog,
profile=FAILING_PROFILE,
query="first health policy metro 15 lakh sum insured zero co-pay",
intent="recommendation",
)
ids = [c["policy_id"] for c in filtered]
self.assertNotIn("royal-sundaram__advanced-top-up", ids,
"super-top-up must not reach the brain")
self.assertNotIn("royal-sundaram__multiplier", ids,
"20%-copay plan must not reach a zero-copay user")
self.assertNotIn("star-health__medi-classic", ids,
"₹5L-max plan must not reach a ₹15L requirement")
self.assertTrue(ids, "good-fit plans must still survive")
# Best-fit (A-grade, zero-copay, offers 15L) should be ranked first.
self.assertEqual(
ids[0], "niva-bupa__reassure-2",
"Top recommendation must be the best profile-fit plan.",
)
def test_demographic_filter_still_runs(self):
# apply_profile_filter (age/senior/maternity) must still be composed.
senior = _chunk("star-health__red-carpet",
"Senior Citizens Red Carpet",
policy_type="indemnity",
sum_insured_options=[1500000])
senior["min_entry_age"] = 60
filtered, _ = filter_pipeline(
[senior, GOOD_FIT_A], profile=FAILING_PROFILE,
query="x", intent="recommendation",
)
ids = [c["policy_id"] for c in filtered]
self.assertNotIn("star-health__red-carpet", ids,
"29yo must not see a senior-only plan")
if __name__ == "__main__":
unittest.main()