Add production condition retrieval smoke test

This commit is contained in:
2026-08-11 14:58:28 +07:00
parent 59e6ad2d0d
commit 7ebbe1f309
38 changed files with 3752 additions and 121 deletions
@@ -1,6 +1,7 @@
from pathlib import Path
from rag.artifacts import load_aliases
from rag.clinical import CandidateStatus, PatientContext, RenalContext
from rag.evaluation import CaseOrigin, EvaluationCase, EvaluationOutcome, summarize
from rag.in_memory import InMemoryLexicalRetriever, InMemoryParentStore, _char_ngrams
from rag.models import (
@@ -519,3 +520,191 @@ def test_retrieve_by_indication_with_blank_text_abstains_without_calling_retriev
assert result.decision == EvidenceDecision.ABSTAIN
assert result.reason == "missing_indication"
def test_indication_candidates_are_ranked_per_drug_not_by_chunk_count():
many_weak = [
SearchHit(
RetrievalDocument(
doc_id=f"drug_many__chi_dinh__{index}",
drug_id="drug_many",
kind="prose",
section_key="chi_dinh",
text="Điều trị tăng huyết áp.",
source_refs=(SOURCE,),
part_index=index,
),
score=1.0,
)
for index in range(8)
]
one_strong = SearchHit(
RetrievalDocument(
doc_id="drug_strong__chi_dinh__0",
drug_id="drug_strong",
kind="prose",
section_key="chi_dinh",
text="Điều trị tăng huyết áp.",
source_refs=(SOURCE,),
),
score=2.0,
)
retriever = _IndicationRetriever(keyword_hits=[*many_weak, one_strong])
service = RetrievalService(
retriever,
InMemoryParentStore([]),
EvidencePolicy(indication_candidate_limit=2, indication_evidence_per_drug=2),
)
result = service.retrieve_by_indication("tăng huyết áp")
assert result.evidence[0].drug_id == "drug_strong"
assert [item.drug_id for item in result.evidence].count("drug_many") == 2
assert len(result.evidence) == 3
class _PatientSafetyRetriever(_IndicationRetriever):
def __init__(self) -> None:
super().__init__(keyword_hits=[_indication_hit("amlodipin")])
self.safety_calls: list[tuple[str, tuple[str, ...]]] = []
def search_lexical(self, query, drug_id, limit, section_keys=None):
self.safety_calls.append((query, section_keys or ()))
hits = [
SearchHit(
RetrievalDocument(
doc_id=f"{drug_id}__than_trong__0",
drug_id=drug_id,
drug_name="AMLODIPIN",
kind="prose",
section_key="than_trong",
section_title="Thận trọng",
text="Thận trọng ở người bệnh suy thận.",
source_refs=(SOURCE,),
),
score=3.0,
),
SearchHit(
RetrievalDocument(
doc_id=f"{drug_id}__tuong_tac_thuoc__0",
drug_id=drug_id,
drug_name="AMLODIPIN",
kind="prose",
section_key="tuong_tac_thuoc",
section_title="Tương tác thuốc",
text="Tương tác được ghi nhận với digoxin.",
source_refs=(SOURCE,),
),
score=2.0,
),
]
return [
hit
for hit in hits
if (not section_keys or hit.document.section_key in section_keys)
and (
hit.document.section_key != "tuong_tac_thuoc"
or "digoxin" in query.casefold()
)
][:limit]
def find_by_section(self, drug_id, section_key):
return []
def test_patient_stage_two_targets_renal_and_current_medication_evidence():
retriever = _PatientSafetyRetriever()
service = RetrievalService(retriever, InMemoryParentStore([]))
indication = service.retrieve_by_indication("tăng huyết áp")
patient = PatientContext(
age_text="68 tuổi",
comorbidities=("CKD G4", "gút"),
current_medications=("digoxin",),
renal=RenalContext(description="CKD", ckd_stage="G4"),
)
result, assessments = service.assess_patient_candidates(indication, patient)
assert result.decision == EvidenceDecision.ANSWERABLE
assert result.reason == "grounded_patient_evidence_available"
assert len(assessments) == 1
assessment = assessments[0]
assert assessment.status == CandidateStatus.SUPPORTED_WITH_CAUTION
assert assessment.renal_evidence
assert assessment.interaction_evidence
searched_sections = {
section
for _, sections in retriever.safety_calls
for section in sections
}
assert "tuong_tac_thuoc" in searched_sections
assert "lieu_luong_va_cach_dung" in searched_sections
def test_patient_interaction_requires_current_drug_match_in_interaction_section():
retriever = _PatientSafetyRetriever()
service = RetrievalService(retriever, InMemoryParentStore([]))
indication = service.retrieve_by_indication("tăng huyết áp")
patient = PatientContext(
current_medications=("warfarin",),
renal=RenalContext(description="CKD", ckd_stage="G4"),
)
_, assessments = service.assess_patient_candidates(indication, patient)
assert len(assessments) == 1
assert assessments[0].interaction_evidence == ()
interaction_calls = [
query
for query, sections in retriever.safety_calls
if sections == ("tuong_tac_thuoc",)
]
assert interaction_calls == ["warfarin"]
def test_matching_contraindication_is_retained_without_declaring_patient_status():
class ContraindicationRetriever(_PatientSafetyRetriever):
def search_lexical(self, query, drug_id, limit, section_keys=None):
hits = super().search_lexical(query, drug_id, limit, section_keys)
if section_keys and "chong_chi_dinh" in section_keys:
hits.append(SearchHit(
RetrievalDocument(
doc_id=f"{drug_id}__chong_chi_dinh__0",
drug_id=drug_id,
drug_name="AMLODIPIN",
kind="prose",
section_key="chong_chi_dinh",
section_title="Chống chỉ định",
text="Chống chỉ định ở người bệnh suy thận nặng.",
source_refs=(SOURCE,),
),
score=1.0,
))
return hits
retriever = ContraindicationRetriever()
service = RetrievalService(retriever, InMemoryParentStore([]))
indication = service.retrieve_by_indication("tăng huyết áp")
_, assessments = service.assess_patient_candidates(
indication,
PatientContext(renal=RenalContext(description="suy thận nặng")),
)
assert assessments[0].status == CandidateStatus.SUPPORTED_WITH_CAUTION
assert assessments[0].contraindication_evidence
def test_named_drug_renal_query_adds_targeted_patient_safety_evidence():
retriever = _PatientSafetyRetriever()
service = RetrievalService(retriever, InMemoryParentStore([]))
base = service.retrieve_by_indication("tăng huyết áp")
patient = PatientContext(
renal=RenalContext(description="suy thận", egfr="25 ml/phút/1,73 m2")
)
result = service.retrieve_patient_drug_context("amlodipin", base, patient)
assert result.decision == EvidenceDecision.ANSWERABLE
assert result.reason == "grounded_patient_evidence_available"
assert {item.section_key for item in result.evidence} >= {"chi_dinh", "than_trong"}