Fix ai-service Dockerfile: bake in drug_entities.json, override its path
This commit is contained in:
@@ -1,4 +1,36 @@
|
||||
from adapters.qdrant import _source_refs
|
||||
from adapters.qdrant import QdrantRetriever, _source_refs
|
||||
|
||||
|
||||
class _FakePoint:
|
||||
def __init__(self, payload: dict) -> None:
|
||||
self.payload = payload
|
||||
self.score = 1.0
|
||||
|
||||
|
||||
class _FakeScrollClient:
|
||||
"""Mimics qdrant-client's `.scroll()` shape closely enough to exercise
|
||||
`find_by_indication`'s keyword-matching logic directly — a fake filter
|
||||
(not a real one), so it returns every payload handed to it regardless
|
||||
of `scroll_filter`; the payloads given in each test already represent
|
||||
what a real `section_key=chi_dinh, chunk_kind=prose` filter would have
|
||||
returned, which is the part `find_by_indication` cannot get wrong on
|
||||
its own (the filter construction itself is a one-line, inspectable
|
||||
`Filter(must=[...])` — not worth a second fake layer to prove)."""
|
||||
|
||||
def __init__(self, payloads: list[dict]) -> None:
|
||||
self._payloads = payloads
|
||||
|
||||
def scroll(self, collection_name, scroll_filter, limit, offset, with_payload): # noqa: ARG002
|
||||
return [_FakePoint(p) for p in self._payloads], None
|
||||
|
||||
|
||||
def _chi_dinh_payload(drug_id: str, text: str) -> dict:
|
||||
return {
|
||||
"chunk_id": f"{drug_id}__chi_dinh__0", "drug_id": drug_id,
|
||||
"drug_name": drug_id.upper(), "section_key": "chi_dinh",
|
||||
"chunk_kind": "prose", "text": text,
|
||||
"heading_physical_page": 100, "printed_page_range": [101, 101],
|
||||
}
|
||||
|
||||
|
||||
def test_descriptor_source_ref_comes_from_attachment_not_heading_page():
|
||||
@@ -45,3 +77,85 @@ def test_prose_ref_uses_exact_chunk_range_and_keeps_attachment_region():
|
||||
assert refs[1].block_id == "p105_t0"
|
||||
assert refs[1].physical_page == 105
|
||||
assert refs[1].printed_page == 106
|
||||
|
||||
|
||||
def test_find_by_indication_matches_a_drug_that_names_the_symptom():
|
||||
client = _FakeScrollClient([
|
||||
_chi_dinh_payload("paracetamol_acetaminophen", "Điều trị sốt, giảm đau nhẹ và vừa."),
|
||||
_chi_dinh_payload("amoxicilin", "Điều trị nhiễm khuẩn đường hô hấp."),
|
||||
])
|
||||
retriever = QdrantRetriever(client, "duocthu_v1", embedder=None)
|
||||
|
||||
hits = retriever.find_by_indication("sốt", limit=8)
|
||||
|
||||
assert [h.document.drug_id for h in hits] == ["paracetamol_acetaminophen"]
|
||||
|
||||
|
||||
def test_find_by_indication_requires_the_whole_phrase_not_a_scattered_match():
|
||||
""""sốt xuất huyết" (dengue) must not match a chunk that only says "sốt"
|
||||
— the phrase itself has to appear, not just each of its words somewhere."""
|
||||
client = _FakeScrollClient([
|
||||
_chi_dinh_payload("paracetamol_acetaminophen", "Điều trị sốt, giảm đau."),
|
||||
])
|
||||
retriever = QdrantRetriever(client, "duocthu_v1", embedder=None)
|
||||
|
||||
hits = retriever.find_by_indication("sốt xuất huyết", limit=8)
|
||||
|
||||
assert hits == []
|
||||
|
||||
|
||||
def test_find_by_indication_matches_a_multi_word_phrase_contiguously():
|
||||
client = _FakeScrollClient([
|
||||
_chi_dinh_payload("paracetamol_acetaminophen", "Điều trị sốt cao ở người lớn."),
|
||||
])
|
||||
retriever = QdrantRetriever(client, "duocthu_v1", embedder=None)
|
||||
|
||||
hits = retriever.find_by_indication("sốt cao", limit=8)
|
||||
|
||||
assert [h.document.drug_id for h in hits] == ["paracetamol_acetaminophen"]
|
||||
|
||||
|
||||
def test_find_by_indication_rejects_a_scattered_bag_of_common_words():
|
||||
"""Found live 2026-08-07: a token-SUBSET match (every word present
|
||||
*somewhere*, any order) let a long nonsense phrase built from common
|
||||
filler words false-positive against real chi_dinh text — the words are
|
||||
common enough to appear scattered through nearly anything. Phrase
|
||||
matching closes it: none of these words are contiguous in the target
|
||||
text the way they are in the query."""
|
||||
client = _FakeScrollClient([
|
||||
_chi_dinh_payload(
|
||||
"paracetamol_acetaminophen",
|
||||
"Điều trị sốt. Không dùng quá liều khuyến cáo trong sách hướng dẫn.",
|
||||
),
|
||||
])
|
||||
retriever = QdrantRetriever(client, "duocthu_v1", embedder=None)
|
||||
|
||||
hits = retriever.find_by_indication(
|
||||
"bệnh chưa từng ghi nhận trong sách abcxyz123", limit=8
|
||||
)
|
||||
|
||||
assert hits == []
|
||||
|
||||
|
||||
def test_find_by_indication_returns_at_most_one_hit_per_drug():
|
||||
client = _FakeScrollClient([
|
||||
_chi_dinh_payload("paracetamol_acetaminophen", "Điều trị sốt."),
|
||||
{**_chi_dinh_payload("paracetamol_acetaminophen", "Điều trị sốt cao ở trẻ em."),
|
||||
"chunk_id": "paracetamol_acetaminophen__chi_dinh__1"},
|
||||
])
|
||||
retriever = QdrantRetriever(client, "duocthu_v1", embedder=None)
|
||||
|
||||
hits = retriever.find_by_indication("sốt", limit=8)
|
||||
|
||||
assert len(hits) == 1
|
||||
|
||||
|
||||
def test_find_by_indication_respects_the_limit():
|
||||
client = _FakeScrollClient([
|
||||
_chi_dinh_payload(f"drug_{i}", "Điều trị đau.") for i in range(5)
|
||||
])
|
||||
retriever = QdrantRetriever(client, "duocthu_v1", embedder=None)
|
||||
|
||||
hits = retriever.find_by_indication("đau", limit=2)
|
||||
|
||||
assert len(hits) == 2
|
||||
|
||||
Reference in New Issue
Block a user