CoolFace
Apppublic

ITNovaML/PCAgentinAI

sourceHugging Faceupdated 5mo agoView on Hugging Face
0likes
agent8_coverage_verify.py174 linesDownload Raw Back to root
1"""2ClaimSense — Agent 8: Coverage Verification Agent3===================================================4Verifies whether the claimed incident is covered under the policy.5- Works for both linked (submission_id present) and standalone flows6- Checks incident type vs coverage type eligibility matrix7- Checks deductible, limits, exclusions8- No ML — pure rules engine9"""10 11import logging12from datetime import datetime, date13 14log = logging.getLogger(__name__)15 16# ── Coverage eligibility matrix ───────────────────────────────17# Maps coverage_type → set of covered incident types18COVERAGE_MATRIX = {19    'HO-3': {20        'FIRE', 'WIND', 'HAIL', 'THEFT', 'VANDALISM',21        'LIABILITY', 'VEHICLE_IMPACT', 'WATER', 'STRUCTURAL', 'OTHER'22    },23    'HO-5': {24        'FIRE', 'WIND', 'HAIL', 'THEFT', 'VANDALISM',25        'LIABILITY', 'VEHICLE_IMPACT', 'WATER', 'STRUCTURAL',26        'EARTHQUAKE', 'OTHER'27    },28    'HO-4': {  # Renters29        'FIRE', 'THEFT', 'VANDALISM', 'LIABILITY', 'WATER', 'OTHER'30    },31    'HO-6': {  # Condo32        'FIRE', 'THEFT', 'VANDALISM', 'LIABILITY', 'WATER', 'STRUCTURAL', 'OTHER'33    },34    'DP-3': {  # Dwelling35        'FIRE', 'WIND', 'HAIL', 'VANDALISM', 'VEHICLE_IMPACT', 'OTHER'36    },37    'WC-3': {38        'LIABILITY', 'OTHER'39    },40}41 42# Incident types typically excluded (require endorsement)43FLOOD_EXCLUDED      = {'HO-3', 'HO-4', 'HO-5', 'HO-6', 'DP-3'}44EARTHQUAKE_EXCLUDED = {'HO-3', 'HO-4', 'HO-6', 'DP-3'}45 46 47def run_coverage_verify_agent(fnol_result: dict, submission: dict) -> dict:48    """49    Agent 8 — Coverage Verification.50 51    Args:52        fnol_result:  Output from Agent 7 (FNOL Intake)53        submission:   Submission/policy data dict (may be empty for standalone)54 55    Returns:56        result dict with coverage_status, applicable_limit, deductible, exclusions57    """58    claim_id      = fnol_result.get('claim_id', '')59    incident_type = fnol_result.get('incident_type', 'OTHER')60    policy_number = fnol_result.get('policy_number', '')61 62    log.info(f"[COVERAGE] Agent 8 running for {claim_id}")63 64    # ── Pull policy details ───────────────────────────────────65    # Works for both linked (from DB via submission) and standalone66    coverage_type = (67        submission.get('_coverage_type_code') or68        submission.get('coverage_type_code') or69        (submission.get('policy_request') or {}).get('coverage_type') or70        'HO-3'  # safe default71    )72 73    coverage_limit = float(74        submission.get('_requested_coverage_limit') or75        (submission.get('policy_request') or {}).get('requested_coverage_limit') or76        30000077    )78    deductible = float(79        submission.get('_requested_deductible') or80        (submission.get('policy_request') or {}).get('requested_deductible') or81        250082    )83    estimated_damage = float(84        fnol_result.get('normalised_fields', {}).get('estimated_damage') or 085    )86 87    coverage_type = str(coverage_type).upper().strip()88    exclusions    = []89    partial_notes = []90 91    # ── Check eligibility matrix ──────────────────────────────92    covered_perils = COVERAGE_MATRIX.get(coverage_type, COVERAGE_MATRIX['HO-3'])93 94    if incident_type == 'FLOOD' and coverage_type in FLOOD_EXCLUDED:95        exclusions.append(96            f"FLOOD damage is excluded under {coverage_type}. "97            "Separate flood insurance (NFIP) required."98        )99    elif incident_type == 'EARTHQUAKE' and coverage_type in EARTHQUAKE_EXCLUDED:100        exclusions.append(101            f"EARTHQUAKE is excluded under {coverage_type}. "102            "Earthquake endorsement required."103        )104    elif incident_type not in covered_perils:105        exclusions.append(106            f"Incident type {incident_type} is not a covered peril under {coverage_type}."107        )108 109    # ── Limit checks ─────────────────────────────────────────110    applicable_limit = coverage_limit111    if estimated_damage > coverage_limit:112        partial_notes.append(113            f"Estimated damage ${estimated_damage:,.0f} exceeds "114            f"coverage limit ${coverage_limit:,.0f}. "115            f"Maximum payable: ${coverage_limit:,.0f}."116        )117 118    # ── Deductible check ──────────────────────────────────────119    net_payable = max(0, min(estimated_damage, coverage_limit) - deductible)120    if estimated_damage > 0 and estimated_damage <= deductible:121        partial_notes.append(122            f"Claimed amount ${estimated_damage:,.0f} does not exceed "123            f"deductible ${deductible:,.0f}. Claim may not result in payment."124        )125 126    # ── Final coverage status ─────────────────────────────────127    if exclusions:128        coverage_status = 'EXCLUDED'129    elif partial_notes:130        coverage_status = 'PARTIAL'131    else:132        coverage_status = 'COVERED'133 134    # ── Summary ───────────────────────────────────────────────135    if coverage_status == 'COVERED':136        summary = (137            f"Incident type {incident_type} is fully covered under {coverage_type}. "138            f"Coverage limit: ${coverage_limit:,.0f}. "139            f"Deductible: ${deductible:,.0f}. "140            f"Estimated net payable: ${net_payable:,.0f}."141        )142    elif coverage_status == 'PARTIAL':143        summary = (144            f"Incident {incident_type} is covered under {coverage_type} "145            f"but with limitations. " + " ".join(partial_notes)146        )147    else:148        summary = (149            f"Incident {incident_type} is NOT covered under {coverage_type}. "150            + " ".join(exclusions)151        )152 153    result = {154        'claim_id':          claim_id,155        'status':            f'COVERAGE_{coverage_status}',156        'coverage_status':   coverage_status,157        'coverage_type':     coverage_type,158        'policy_number':     policy_number,159        'applicable_limit':  applicable_limit,160        'deductible':        deductible,161        'estimated_damage':  estimated_damage,162        'net_payable_est':   net_payable,163        'exclusions':        exclusions,164        'partial_notes':     partial_notes,165        'summary':           summary,166    }167 168    log.info(169        f"[COVERAGE] {claim_id}: {coverage_status} | "170        f"type={coverage_type} | limit=${coverage_limit:,.0f} | "171        f"exclusions={len(exclusions)}"172    )173    return result174