feat(rag): lite-model document summaries — non-markdown docs summarized at import, summary chunk retrieves and resolves to the full source doc
This commit is contained in:
@@ -47,18 +47,23 @@ def _doc(title: str, content: str) -> Document:
|
||||
|
||||
|
||||
def _chunk(
|
||||
doc: Document, score: float, cosine: float | None = None, fts_hit: bool = False
|
||||
doc: Document,
|
||||
score: float,
|
||||
cosine: float | None = None,
|
||||
fts_hit: bool = False,
|
||||
is_summary: bool = False,
|
||||
) -> RetrievedChunk:
|
||||
"""Fake candidate: *score* is the fused rank score; *cosine* (defaults to
|
||||
*score*) is the vector-similarity gate input."""
|
||||
return RetrievedChunk(
|
||||
chunk_id=uuid.uuid4(),
|
||||
position=0,
|
||||
position=-1 if is_summary else 0,
|
||||
content=doc.content[:32],
|
||||
score=score,
|
||||
document=doc,
|
||||
cosine=score if cosine is None else cosine,
|
||||
fts_hit=fts_hit,
|
||||
is_summary=is_summary,
|
||||
)
|
||||
|
||||
|
||||
@@ -167,6 +172,70 @@ def test_gate_zero_chunks_deflects_with_fallback_chips() -> None:
|
||||
assert 2 <= len(plan.suggestions) <= MAX_SUGGESTIONS
|
||||
|
||||
|
||||
# ---------- summary hits (phase 30: summary → full source document) ----------
|
||||
|
||||
|
||||
def test_summary_hit_on_selected_top_doc_counts() -> None:
|
||||
"""HIGH branch: the top document was hit via its summary chunk ⇒ 1.
|
||||
|
||||
Context assembly is unchanged (A7 revised): the *source* document's
|
||||
full content lands in the prompt, not the summary text alone.
|
||||
"""
|
||||
a = _doc("Alpha", "ALPHA_FULL_SOURCE_CONTENT")
|
||||
b = _doc("Beta", "BETA_FULL_SOURCE_CONTENT")
|
||||
chunks = [
|
||||
_chunk(a, 0.90, is_summary=True), # top doc reached through its summary
|
||||
_chunk(b, 0.50),
|
||||
]
|
||||
plan = chat_api.plan_turn(chunks, _settings(threshold=0.30))
|
||||
assert plan.deflected is False
|
||||
assert plan.summary_hits == 1
|
||||
# The full source document is what the LLM sees (phase 24 contract).
|
||||
assert "ALPHA_FULL_SOURCE_CONTENT" in plan.system_prompt
|
||||
|
||||
|
||||
def test_summary_hit_outside_top_n_selection_not_counted() -> None:
|
||||
"""A summary chunk on a document outside the top-N (default 2) selection
|
||||
does not count — only hits that landed in the selected context do."""
|
||||
a = _doc("Alpha", "ALPHA_CONTENT")
|
||||
b = _doc("Beta", "BETA_CONTENT")
|
||||
c = _doc("Gamma", "GAMMA_CONTENT")
|
||||
chunks = [
|
||||
_chunk(a, 0.90),
|
||||
_chunk(b, 0.80),
|
||||
_chunk(c, 0.70, is_summary=True), # 3rd-ranked doc — not selected
|
||||
]
|
||||
plan = chat_api.plan_turn(chunks, _settings(threshold=0.30))
|
||||
assert plan.deflected is False
|
||||
assert [d.title for d in plan.docs] == ["Alpha", "Beta"]
|
||||
assert plan.summary_hits == 0
|
||||
|
||||
|
||||
def test_low_branch_counts_summary_hit_on_selected_doc() -> None:
|
||||
"""LOW (deflected) branch records ``summary_hits`` too: the weak hit's
|
||||
parent is still the selected (weak-hit) document."""
|
||||
a = _doc("Gamma", "GAMMA_DOC_CONTENT")
|
||||
b = _doc("Delta", "DELTA_DOC_CONTENT")
|
||||
chunks = [
|
||||
_chunk(a, 0.05, cosine=0.05, is_summary=True), # weak cosine, no FTS
|
||||
_chunk(b, 0.03, cosine=0.03),
|
||||
]
|
||||
plan = chat_api.plan_turn(chunks, _settings(threshold=0.30))
|
||||
assert plan.deflected is True
|
||||
assert plan.summary_hits == 1
|
||||
|
||||
|
||||
def test_no_summary_chunks_yields_zero_summary_hits() -> None:
|
||||
"""Legacy chunks (``is_summary=false``) keep ``summary_hits == 0``."""
|
||||
a = _doc("Alpha", "ALPHA_CONTENT")
|
||||
b = _doc("Beta", "BETA_CONTENT")
|
||||
plan = chat_api.plan_turn([_chunk(a, 0.90), _chunk(b, 0.40)], _settings(threshold=0.30))
|
||||
assert plan.summary_hits == 0
|
||||
plan_low = chat_api.plan_turn([_chunk(a, 0.05, cosine=0.05)], _settings(threshold=0.30))
|
||||
assert plan_low.deflected is True
|
||||
assert plan_low.summary_hits == 0
|
||||
|
||||
|
||||
# ---------- prompt content (LOW vs HIGH) ----------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user