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:
2026-08-25 17:48:37 -04:00
parent 9809482a4b
commit 572a4190a6
32 changed files with 1806 additions and 26 deletions
+12 -1
View File
@@ -56,6 +56,7 @@ _LEXICAL_SQL = text(
d.content AS doc_content,
d.content_hash AS content_hash,
d.indexed_at AS indexed_at,
c.is_summary AS is_summary,
ts_rank(c.tsv, to_tsquery('english', :tsquery)) AS rank
FROM chunks c
JOIN documents d ON d.id = c.document_id
@@ -75,6 +76,11 @@ class RetrievedChunk:
* ``cosine`` — vector similarity ``1 − distance`` (the honesty-gate
input; ``0.0`` for lexical-only hits that have no vector rank).
* ``fts_hit`` — the chunk matched the question's OR-tsquery.
* ``is_summary`` — True for the lite-model summary chunk (phase 30,
position −1): its parent *is* the source document, so a summary hit
resolves to the full source document through the unchanged
chunk→document mapping (A7 revised). Default ``False`` keeps every
ordinary content chunk valid.
"""
chunk_id: uuid.UUID
@@ -84,6 +90,7 @@ class RetrievedChunk:
document: Document
cosine: float = 0.0
fts_hit: bool = False
is_summary: bool = False
def lexical_tsquery(question: str) -> str | None:
@@ -148,7 +155,9 @@ def _vector_candidates(
"""Top-*limit* chunks by pgvector cosine distance (``<=>``).
``cosine = 1 − distance``. Chunks whose embedding is still NULL
(two-phase import in progress) are skipped.
(two-phase import in progress) are skipped. Each candidate carries
its ``Chunk.is_summary`` flag (phase 30) so a summary hit stays
identifiable after fusion.
"""
distance = Chunk.embedding.cosine_distance(question_embedding)
rows = db.execute(
@@ -166,6 +175,7 @@ def _vector_candidates(
score=0.0, # fused score is filled in by :func:`fuse`
document=doc,
cosine=round(1.0 - float(dist), 6),
is_summary=chunk.is_summary,
)
for chunk, dist, doc in rows
]
@@ -205,6 +215,7 @@ def _lexical_candidates(db: Session, question: str, limit: int) -> list[Retrieve
document=doc,
cosine=0.0, # no vector rank — lexical-only hit
fts_hit=True,
is_summary=row.is_summary,
)
)
return out