feat: mode hybride Ollama — gemma3:27b pour CPAM, 12b pour codage
Le pipeline utilise désormais gemma3:12b (rapide) pour le codage CIM-10 et gemma3:27b (meilleur raisonnement) pour la contre-argumentation CPAM. Configurable via OLLAMA_MODEL_CPAM et OLLAMA_TIMEOUT_CPAM. Inclut aussi : traçabilité source/page DAS, niveaux CMA ATIH, sévérité, page tracker PDF, améliorations fusion et filtres DAS. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ from src.medical.fusion import (
|
||||
_dedup_actes,
|
||||
_is_enriched,
|
||||
)
|
||||
from src.medical.das_filter import apply_semantic_dedup
|
||||
|
||||
|
||||
class TestCIM10Specificity:
|
||||
@@ -354,3 +355,139 @@ class TestDedupPreferEnriched:
|
||||
result = _dedup_diagnostics(das)
|
||||
assert len(result) == 1
|
||||
assert result[0].cim10_confidence == "high"
|
||||
|
||||
|
||||
class TestDasFamilyDpRemoved:
|
||||
"""Vérifie la dédup DAS vs DP par famille CIM-10 (3 premiers caractères)."""
|
||||
|
||||
def test_same_family_removed(self):
|
||||
"""DP=K85.1, DAS=[K85.0, K85.9, E66.0] → seul E66.0 reste."""
|
||||
d1 = DossierMedical(
|
||||
diagnostic_principal=Diagnostic(texte="Pancréatite biliaire", cim10_suggestion="K85.1"),
|
||||
diagnostics_associes=[
|
||||
Diagnostic(texte="Pancréatite SAI", cim10_suggestion="K85.0"),
|
||||
Diagnostic(texte="Pancréatite aiguë", cim10_suggestion="K85.9"),
|
||||
Diagnostic(texte="Obésité", cim10_suggestion="E66.0"),
|
||||
],
|
||||
)
|
||||
result = merge_dossiers([d1])
|
||||
das_codes = {d.cim10_suggestion for d in result.diagnostics_associes}
|
||||
assert "K85.0" not in das_codes
|
||||
assert "K85.9" not in das_codes
|
||||
assert "E66.0" in das_codes
|
||||
|
||||
def test_trauma_siblings_kept(self):
|
||||
"""S/T : sites anatomiques différents → tous gardés."""
|
||||
d1 = DossierMedical(
|
||||
diagnostic_principal=Diagnostic(texte="Fracture col fémoral", cim10_suggestion="S72.1"),
|
||||
diagnostics_associes=[
|
||||
Diagnostic(texte="Fracture trochanter", cim10_suggestion="S72.0"),
|
||||
Diagnostic(texte="Fracture sous-troch", cim10_suggestion="S72.3"),
|
||||
],
|
||||
)
|
||||
result = merge_dossiers([d1])
|
||||
das_codes = {d.cim10_suggestion for d in result.diagnostics_associes}
|
||||
assert "S72.0" in das_codes
|
||||
assert "S72.3" in das_codes
|
||||
|
||||
def test_diabetes_complications_kept(self):
|
||||
"""E10-E14 : complications distinctes → tous gardés."""
|
||||
d1 = DossierMedical(
|
||||
diagnostic_principal=Diagnostic(texte="Diabète avec complications oculaires", cim10_suggestion="E11.6"),
|
||||
diagnostics_associes=[
|
||||
Diagnostic(texte="Diabète avec complications rénales", cim10_suggestion="E11.2"),
|
||||
Diagnostic(texte="HTA essentielle", cim10_suggestion="I10"),
|
||||
],
|
||||
)
|
||||
result = merge_dossiers([d1])
|
||||
das_codes = {d.cim10_suggestion for d in result.diagnostics_associes}
|
||||
assert "E11.2" in das_codes
|
||||
assert "I10" in das_codes
|
||||
|
||||
def test_parent_child_removed(self):
|
||||
"""DP=K85.1, DAS=[K85] → K85 (parent) retiré."""
|
||||
d1 = DossierMedical(
|
||||
diagnostic_principal=Diagnostic(texte="Pancréatite biliaire", cim10_suggestion="K85.1"),
|
||||
diagnostics_associes=[
|
||||
Diagnostic(texte="Pancréatite", cim10_suggestion="K85"),
|
||||
],
|
||||
)
|
||||
result = merge_dossiers([d1])
|
||||
das_codes = {d.cim10_suggestion for d in result.diagnostics_associes}
|
||||
assert len(das_codes) == 0
|
||||
|
||||
def test_ocr_dp_not_promoted(self):
|
||||
"""Fusion avec DP artefact OCR 'À 09' → pas promu en DAS."""
|
||||
d1 = DossierMedical(
|
||||
diagnostic_principal=Diagnostic(texte="Pancréatite biliaire", cim10_suggestion="K85.1"),
|
||||
)
|
||||
d2 = DossierMedical(
|
||||
diagnostic_principal=Diagnostic(texte="À 09", cim10_suggestion="A41.9"),
|
||||
)
|
||||
result = merge_dossiers([d1, d2])
|
||||
das_codes = {d.cim10_suggestion for d in result.diagnostics_associes}
|
||||
assert "A41.9" not in das_codes
|
||||
|
||||
|
||||
class TestSemanticDedup:
|
||||
"""Vérifie les redondances sémantiques entre DAS."""
|
||||
|
||||
def test_i10_removed_when_i11_present(self):
|
||||
"""I10 (HTA essentielle) retiré si I11.9 (cardiopathie hypertensive) présent."""
|
||||
das = [
|
||||
Diagnostic(texte="HTA essentielle", cim10_suggestion="I10"),
|
||||
Diagnostic(texte="Cardiopathie hypertensive", cim10_suggestion="I11.9"),
|
||||
Diagnostic(texte="Obésité", cim10_suggestion="E66.0"),
|
||||
]
|
||||
result = apply_semantic_dedup(das)
|
||||
codes = {d.cim10_suggestion for d in result}
|
||||
assert "I10" not in codes
|
||||
assert "I11.9" in codes
|
||||
assert "E66.0" in codes
|
||||
|
||||
def test_n30_removed_when_n39_present(self):
|
||||
"""N30.9 (cystite) retiré si N39.0 (infection urinaire) présent."""
|
||||
das = [
|
||||
Diagnostic(texte="Infection urinaire", cim10_suggestion="N39.0"),
|
||||
Diagnostic(texte="Cystite SAI", cim10_suggestion="N30.9"),
|
||||
]
|
||||
result = apply_semantic_dedup(das)
|
||||
codes = {d.cim10_suggestion for d in result}
|
||||
assert "N39.0" in codes
|
||||
assert "N30.9" not in codes
|
||||
|
||||
def test_j18_removed_when_j15_present(self):
|
||||
"""J18.9 (pneumonie SAI) retiré si J15.1 (pneumonie spécifique) présent."""
|
||||
das = [
|
||||
Diagnostic(texte="Pneumonie SAI", cim10_suggestion="J18.9"),
|
||||
Diagnostic(texte="Pneumonie à Klebsiella", cim10_suggestion="J15.1"),
|
||||
]
|
||||
result = apply_semantic_dedup(das)
|
||||
codes = {d.cim10_suggestion for d in result}
|
||||
assert "J15.1" in codes
|
||||
assert "J18.9" not in codes
|
||||
|
||||
def test_no_removal_without_dominant(self):
|
||||
"""I10 conservé si aucun code dominant I11/I12/I13."""
|
||||
das = [
|
||||
Diagnostic(texte="HTA essentielle", cim10_suggestion="I10"),
|
||||
Diagnostic(texte="Obésité", cim10_suggestion="E66.0"),
|
||||
]
|
||||
result = apply_semantic_dedup(das)
|
||||
codes = {d.cim10_suggestion for d in result}
|
||||
assert "I10" in codes
|
||||
assert "E66.0" in codes
|
||||
|
||||
def test_semantic_dedup_in_merge(self):
|
||||
"""Vérifie que la dédup sémantique est appliquée lors de la fusion."""
|
||||
d1 = DossierMedical(
|
||||
diagnostic_principal=Diagnostic(texte="Sepsis", cim10_suggestion="A41.9"),
|
||||
diagnostics_associes=[
|
||||
Diagnostic(texte="HTA essentielle", cim10_suggestion="I10"),
|
||||
Diagnostic(texte="Cardiopathie hypertensive", cim10_suggestion="I11.9"),
|
||||
],
|
||||
)
|
||||
result = merge_dossiers([d1])
|
||||
das_codes = {d.cim10_suggestion for d in result.diagnostics_associes}
|
||||
assert "I10" not in das_codes
|
||||
assert "I11.9" in das_codes
|
||||
|
||||
Reference in New Issue
Block a user