Wire the guarded conversational RAG answer layer end-to-end

This commit is contained in:
2026-08-05 14:33:13 +07:00
parent 834d9e51b0
commit ef08b4929e
127 changed files with 37921 additions and 169 deletions
+119
View File
@@ -1,3 +1,5 @@
from dataclasses import replace
import pytest
from ingestion.extract.models import Span
@@ -50,6 +52,55 @@ def test_non_bold_combined_heading_value_span_confirmed_real_amitriptylin_case()
m = list(assemble(spans))[0]
assert m.sections["ma_atc"].text == "N06AA09."
assert m.atc_codes == ["N06AA09"]
part = m.sections["ma_atc"].parts[0]
assert part.physical_page == 184
assert part.bbox != [0.0, 0.0, 0.0, 0.0]
assert part.source_span_ids == [spans[3].span_id]
def test_combined_international_name_and_atc_heading_is_a_title_anchor():
# Confirmed real GnRH class-monograph variant on physical page 1371.
spans = [
_span("THUỐC TƯƠNG TỰ HORMON GIẢI PHÓNG", 1371, 664.0),
_span("GONADOTROPIN", 1371, 676.0),
_span("Tên chung quốc tế và mã ATC", 1371, 690.0),
_span("Gonadorelin: H01CA01; Triptorelin: L02AE04.", 1371, 702.0, bold=False),
_span("Chỉ định", 1371, 714.0),
_span("Kích thích phóng noãn.", 1371, 726.0, bold=False),
]
m = list(assemble(spans))[0]
assert m.drug_name == "THUỐC TƯƠNG TỰ HORMON GIẢI PHÓNG GONADOTROPIN"
assert m.atc_codes == ["H01CA01", "L02AE04"]
def test_plain_wrapped_section_label_is_body_not_a_heading():
# NADROPARIN CALCI p1016: "không phải là" / "chống chỉ định."
# are adjacent lines of one sentence in the same PDF block.
spans = [
_span("NADROPARIN CALCI", 1016, 60.0),
_span("Tên chung quốc tế", 1016, 80.0),
_span("Nadroparin calcium.", 1016, 92.0, bold=False),
_span("Thời kỳ cho con bú", 1016, 110.0),
]
lead = replace(_span("Việc dùng thuốc không phải là", 1016, 122.0, bold=False),
block=4, line=7)
tail = replace(_span("chống chỉ định.", 1016, 134.0, bold=False),
block=4, line=8)
m = list(assemble(spans + [lead, tail]))[0]
assert m.sections["thoi_ky_cho_con_bu"].text.endswith("chống chỉ định.")
assert "chong_chi_dinh" not in m.sections
def test_plain_heading_after_completed_prose_still_opens_section():
spans = [
_span("TESTDRUG", 300, 60.0),
_span("Tên chung quốc tế", 300, 80.0),
replace(_span("Testdrug.", 300, 92.0, bold=False), block=2, line=0),
replace(_span("Chỉ định", 300, 104.0, bold=False), block=2, line=1),
replace(_span("Điều trị thử nghiệm.", 300, 116.0, bold=False), block=2, line=2),
]
m = list(assemble(spans))[0]
assert m.sections["chi_dinh"].text == "Điều trị thử nghiệm."
def test_atc_stated_absent_propagates():
@@ -330,3 +381,71 @@ def test_a_bold_label_line_still_opens_its_section():
]
monograph = list(assemble(spans))[0]
assert monograph.sections["chong_chi_dinh"].text == "Suy tủy nặng."
def test_a_section_name_printed_mid_line_is_body_not_a_heading():
"""CISPLATIN, physical page 402 — confirmed content loss.
The book prints "Suy thận: Chống chỉ định." inside the dosing section. The
second half is itself a section name, so it was matched as a heading: the
renal-impairment contraindication vanished from the dosing text and the
section ended on a bare "Suy thận:". ISOPRENALIN had the same shape. A
real heading opens its line; this one does not.
"""
spans = [
_span("CISPLATIN", 401, 60.0),
_span("Tên chung quốc tế", 401, 80.0),
_span("Cisplatinum.", 401, 92.0, bold=False),
_span("Liều lượng và cách dùng", 401, 110.0),
_span("Truyền tĩnh mạch mỗi 3 tuần.", 401, 122.0, bold=False),
]
label = _span("Suy thận: ", 401, 140.0, bold=False)
label = replace(label, block=4, line=0, x0=35.0, x1=70.0)
trailing = _span("Chống chỉ định.", 401, 140.0, bold=False)
trailing = replace(trailing, block=4, line=0, x0=70.0, x1=140.0)
monograph = list(assemble(spans + [label, trailing]))[0]
dosing = monograph.sections["lieu_luong_va_cach_dung"].text
assert "Suy thận: Chống chỉ định." in dosing
assert "chong_chi_dinh" not in monograph.sections
def test_italic_cross_reference_overlapping_its_neighbour_by_a_hairline_is_body():
"""NEVIRAPIN, physical page 1045 — confirmed misassignment, whole-corpus.
The book prints `Xem thêm mục ` (x1=104.89) immediately before an italic
`Liều lượng và cách dùng` (x0=104.88): the trailing space's advance width
makes the neighbour end 0.01pt *after* the cross-reference starts. An
end-before-start test therefore read a mid-line cross-reference as a
heading. Same shape, same cause, in CALCI LACTAT (p296, `xem thêm mục
Tương tác thuốc`, 0.02pt) and CEFAZOLIN (p344, `ghi ở mục: Dạng thuốc và
hàm lượng.`), where 4,533 characters of adult dosing were filed under
dosage forms.
"""
spans = [
_span("NEVIRAPIN", 1044, 60.0),
_span("Tên chung quốc tế", 1044, 80.0),
_span("Nevirapine.", 1044, 92.0, bold=False),
_span("Hướng dẫn cách xử trí ADR", 1044, 110.0),
_span("Điều trị các phản ứng bất lợi theo triệu chứng.", 1044, 122.0, bold=False),
]
lead = replace(_span("Xem thêm mục ", 1044, 140.0, bold=False),
block=4, line=0, x0=43.94, x1=104.89)
reference = replace(_span("Liều lượng và cách dùng", 1044, 140.0, bold=False),
block=4, line=0, x0=104.88, x1=199.57)
monograph = list(assemble(spans + [lead, reference]))[0]
assert "Xem thêm mục Liều lượng và cách dùng" in monograph.sections["huong_dan_xu_tri_adr"].text
assert "lieu_luong_va_cach_dung" not in monograph.sections
def test_a_section_name_opening_its_own_line_is_still_a_heading():
spans = [
_span("CISPLATIN", 401, 60.0),
_span("Tên chung quốc tế", 401, 80.0),
_span("Cisplatinum.", 401, 92.0, bold=False),
_span("Chống chỉ định", 401, 110.0),
_span("Suy tủy nặng.", 401, 122.0, bold=False),
]
monograph = list(assemble(spans))[0]
assert monograph.sections["chong_chi_dinh"].text == "Suy tủy nặng."