feat: dp_finalizer — arbitrage Trackare vs CRH-only avec traçabilité audit
Nouveau module src/medical/dp_finalizer.py : - 5 règles d'arbitrage (R1-R5) : CRH CONFIRMED override, Trackare corroboré, symptôme R* override/review, ambigu REVIEW, Z-code/R-code interdits auto-confirm - Traçabilité : dp_trackare, dp_crh_only, dp_final sur DossierMedical - quality_flags dict (merge sans écraser) + alertes_codage (append) Modèles config.py : - DPCandidate, DPSelection (NUKE-3) - get_dp_ranker_llm_enabled(), check_adversarial_model_config() - Champs DossierMedical : dp_trackare, dp_crh_only, dp_final, quality_flags Intégration : - main.py : appel finalize_dp() après vetos/GHM (individuel + fusionné) - benchmark : finalizer dans _rebuild_and_select(), dp_final dans output Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -68,6 +68,52 @@ def get_model(role: str) -> str:
|
||||
return OLLAMA_MODELS.get(role, OLLAMA_MODEL)
|
||||
|
||||
|
||||
# --- Flag LLM pour le sélecteur DP (NUKE-3) ---
|
||||
# Nom canonique : T2A_DP_RANKER_LLM (0/1)
|
||||
# Ancien nom accepté (compat) : DP_RANKER_LLM_ENABLED
|
||||
DP_RANKER_LLM_ENABLED = os.environ.get("T2A_DP_RANKER_LLM", "1").lower() in ("1", "true", "yes")
|
||||
|
||||
|
||||
def get_dp_ranker_llm_enabled() -> bool:
|
||||
"""Retourne l'état du flag LLM pour NUKE-3 (lecture fraîche de l'env).
|
||||
|
||||
Nom canonique : T2A_DP_RANKER_LLM (0/1/true/false/yes/no).
|
||||
Accepte aussi l'ancien nom DP_RANKER_LLM_ENABLED avec warning.
|
||||
"""
|
||||
canonical = os.environ.get("T2A_DP_RANKER_LLM")
|
||||
legacy = os.environ.get("DP_RANKER_LLM_ENABLED")
|
||||
|
||||
if canonical is not None:
|
||||
return canonical.lower() in ("1", "true", "yes")
|
||||
|
||||
if legacy is not None:
|
||||
import logging as _logging
|
||||
_logging.getLogger(__name__).warning(
|
||||
"Env var DP_RANKER_LLM_ENABLED est dépréciée — utiliser T2A_DP_RANKER_LLM"
|
||||
)
|
||||
return legacy.lower() in ("1", "true", "yes")
|
||||
|
||||
# Défaut : activé
|
||||
return True
|
||||
|
||||
|
||||
def check_adversarial_model_config() -> tuple[bool, str]:
|
||||
"""LOGIC-3 — Vérifie si les modèles CPAM et validation sont identiques.
|
||||
|
||||
Returns:
|
||||
(same_model, warning_message)
|
||||
"""
|
||||
cpam = OLLAMA_MODELS.get("cpam", "")
|
||||
validation = OLLAMA_MODELS.get("validation", "")
|
||||
if cpam and validation and cpam == validation:
|
||||
msg = (
|
||||
f"Modèles CPAM et validation identiques ({cpam}) "
|
||||
"— validation adversariale dégradée"
|
||||
)
|
||||
return True, msg
|
||||
return False, ""
|
||||
|
||||
|
||||
# --- Configuration RUM / établissement ---
|
||||
|
||||
FINESS = os.environ.get("T2A_FINESS", "000000000")
|
||||
@@ -553,6 +599,37 @@ class CodeDecision(BaseModel):
|
||||
applied_rules: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class DPCandidate(BaseModel):
|
||||
"""Candidat DP pour la sélection NUKE-3."""
|
||||
|
||||
index: int
|
||||
term: str
|
||||
code: Optional[str] = None
|
||||
confidence: Optional[str] = None
|
||||
source: Optional[str] = None
|
||||
is_comorbidity_like: bool = False
|
||||
is_symptom_like: bool = False
|
||||
is_act_only: bool = False
|
||||
section_strength: int = 0
|
||||
num_occurrences: int = 1
|
||||
score: float = 0.0
|
||||
score_details: dict = Field(default_factory=dict)
|
||||
|
||||
|
||||
class DPSelection(BaseModel):
|
||||
"""Résultat de la sélection NUKE-3 du DP."""
|
||||
|
||||
chosen_index: Optional[int] = None
|
||||
chosen_term: Optional[str] = None
|
||||
chosen_code: Optional[str] = None
|
||||
confidence: Optional[str] = None
|
||||
verdict: str = "REVIEW" # CONFIRMED | REVIEW
|
||||
evidence: list[str] = Field(default_factory=list)
|
||||
reason: Optional[str] = None
|
||||
candidates: list[DPCandidate] = Field(default_factory=list)
|
||||
debug_scores: Optional[dict] = None
|
||||
|
||||
|
||||
class Diagnostic(BaseModel):
|
||||
texte: str
|
||||
cim10_suggestion: Optional[str] = None
|
||||
@@ -656,6 +733,12 @@ class DossierMedical(BaseModel):
|
||||
document_type: str = ""
|
||||
sejour: Sejour = Field(default_factory=Sejour)
|
||||
diagnostic_principal: Optional[Diagnostic] = None
|
||||
dp_selection: Optional[DPSelection] = None
|
||||
# Traçabilité DP (finalizer) — audit DIM
|
||||
dp_trackare: Optional[DPSelection] = None # DP issu du Trackare (si existant)
|
||||
dp_crh_only: Optional[DPSelection] = None # DP issu du CRH-only pipeline
|
||||
dp_final: Optional[DPSelection] = None # DP final après arbitrage finalizer
|
||||
quality_flags: dict = Field(default_factory=dict)
|
||||
diagnostics_associes: list[Diagnostic] = Field(default_factory=list)
|
||||
actes_ccam: list[ActeCCAM] = Field(default_factory=list)
|
||||
antecedents: list[Antecedent] = Field(default_factory=list)
|
||||
|
||||
Reference in New Issue
Block a user