"""Deterministic relevance: how a presented clue relates to a suspect. This is computed from ground truth, never from the model. ``BREAKING`` means the clue is one that an anchored lie of this suspect explicitly breaks on - the only way a lie is ever caught. """ from __future__ import annotations from dataclasses import dataclass from ..schemas.case import CaseFile from ..schemas.enums import Relevance from ..schemas.suspect import Suspect @dataclass(frozen=True) class RelevanceResult: relevance: Relevance broken_lie_id: str | None = None def assess_relevance(case: CaseFile, suspect: Suspect, clue_id: str | None) -> RelevanceResult: if clue_id is None: return RelevanceResult(Relevance.NONE) try: clue = case.clue(clue_id) except KeyError: return RelevanceResult(Relevance.NONE) # Breaking: this clue defeats one of the suspect's anchored lies. for lie in suspect.anchored_lies: if clue_id in lie.breaks_on: return RelevanceResult(Relevance.BREAKING, broken_lie_id=lie.lie_id) if clue.contradicts_alibi_of == suspect.sus_id: return RelevanceResult(Relevance.BREAKING) if clue.points_to_sus_id == suspect.sus_id: return RelevanceResult(Relevance.DIRECT) if clue.supports_fact_id and clue.supports_fact_id in suspect.knows_facts: return RelevanceResult(Relevance.DIRECT) return RelevanceResult(Relevance.TANGENTIAL)