Wire up query history: localStorage session persistence + sidebar UI

This commit is contained in:
2026-08-14 17:44:36 +07:00
parent 9be5819710
commit 057d4ed9dc
23 changed files with 1231 additions and 30 deletions
+57 -5
View File
@@ -110,6 +110,58 @@ def test_golden_named_drug_section_overrides_a_misclassified_relation_frame():
assert frame.needs_clarify is False
def test_two_sections_named_at_once_clarifies_instead_of_silently_narrowing():
"""Regression for `abstain/incomplete_answer` reproduced live 2026-08-14
on "Chỉ định và chống chỉ định của Aspirin là gì?": the turn used to
silently collapse to whichever one section `_apply_named_drug_cues`
picked (chống chỉ định, being the longer phrase), so retrieval only
fetched that section's evidence while the question handed to generation
still promised both — a real, quote-backed completeness gap the
generator could never close. Must now clarify instead."""
understander = LlmQueryUnderstander(_FixedLlm({
"turn_type": "drug_attribute",
"drugs": ["paracetamol_acetaminophen"],
"unknown_drugs": [],
"attribute": "chong_chi_dinh",
"population": None,
"weight_kg": None,
"age_text": None,
"indication": None,
"needs_clarify": False,
"clarify_reason": None,
}), CATALOG, RESOLVER)
frame = understander.understand(
"Chỉ định và chống chỉ định của Paracetamol là gì?"
)
assert frame.turn_type == "drug_attribute"
assert frame.drugs == ("paracetamol_acetaminophen",)
assert frame.attribute is None
assert frame.needs_clarify is True
assert set(frame.quick_replies) == {"Chỉ định", "Chống chỉ định"}
def test_single_section_named_is_unaffected_by_the_multi_section_clarify():
understander = LlmQueryUnderstander(_FixedLlm({
"turn_type": "drug_attribute",
"drugs": ["paracetamol_acetaminophen"],
"unknown_drugs": [],
"attribute": "chong_chi_dinh",
"population": None,
"weight_kg": None,
"age_text": None,
"indication": None,
"needs_clarify": False,
"clarify_reason": None,
}), CATALOG, RESOLVER)
frame = understander.understand("Chống chỉ định của Paracetamol là gì?")
assert frame.attribute == "chong_chi_dinh"
assert frame.needs_clarify is False
def test_exact_candidate_does_not_repeat_the_catalog_wide_fuzzy_scan():
resolver = _FakeResolver({"metformin": "metformin"})
understander = LlmQueryUnderstander(_FixedLlm({
@@ -244,11 +296,11 @@ def test_quick_replies_are_parsed_when_the_model_offers_them():
def test_quick_replies_are_dynamic_but_bounded_before_becoming_ui_chips():
# 19 distinct valid entries (after " Người lớn " / "người lớn" dedup
# and the non-string 12 are dropped) so the 18-item cap — one per
# 20 distinct valid entries (after " Người lớn " / "người lớn" dedup
# and the non-string 12 are dropped) so the 19-item cap — one per
# monograph section, see rag/sections.py SECTION_ORDER — still trims
# the last one, not just the old 4-item cap.
extra = [f"Lựa chọn {i}" for i in range(6, 20)]
extra = [f"Lựa chọn {i}" for i in range(6, 21)]
understander = LlmQueryUnderstander(_FixedLlm({
"turn_type": "dosing_calc", "drugs": ["paracetamol_acetaminophen"],
"unknown_drugs": [], "attribute": None, "population": None,
@@ -263,11 +315,11 @@ def test_quick_replies_are_dynamic_but_bounded_before_becoming_ui_chips():
frame = understander.understand("liều paracetamol")
assert len(frame.quick_replies) == 18
assert len(frame.quick_replies) == 19
assert frame.quick_replies[:5] == (
"Người lớn", "Trẻ em", "Phụ nữ có thai", "Người cao tuổi", "Lựa chọn thứ năm"
)
assert "Lựa chọn 19" not in frame.quick_replies
assert "Lựa chọn 20" not in frame.quick_replies
def test_string_false_does_not_turn_into_a_clarification():