95 lines
4.0 KiB
Python
95 lines
4.0 KiB
Python
from __future__ import annotations
|
|
|
|
from app.engine.persistent_index import PersistentCorpusIndex
|
|
from app.engine.provenance import CorpusStore, DocumentRecord, SegmentRecord
|
|
|
|
|
|
def _segment(segment_id: str, text: str) -> SegmentRecord:
|
|
return SegmentRecord(
|
|
segment_id=segment_id,
|
|
document_id="doc-1",
|
|
text=text,
|
|
ordinal=segment_id,
|
|
char_start=0,
|
|
char_end=len(text),
|
|
source_locator=f"book.json#{segment_id}",
|
|
)
|
|
|
|
|
|
def test_store_deduplicates_same_text_per_document(tmp_path):
|
|
store = CorpusStore(tmp_path / "corpus.sqlite3")
|
|
store.upsert_document(DocumentRecord(document_id="doc-1", title="책"))
|
|
first = _segment("seg-1", "같은 원문입니다.")
|
|
second = _segment("seg-2", "같은 원문입니다.")
|
|
assert store.add_segments([first, second]) == (1, 1)
|
|
assert store.stats()["segments"] == 1
|
|
|
|
|
|
def test_index_sync_appends_without_rebuilding(tmp_path):
|
|
db = tmp_path / "corpus.sqlite3"
|
|
index_dir = tmp_path / "index"
|
|
store = CorpusStore(db)
|
|
store.upsert_document(DocumentRecord(document_id="doc-1", title="책"))
|
|
store.add_segments([_segment("seg-1", "나는 어린 시절 바닷가 마을에서 살았다.")])
|
|
index = PersistentCorpusIndex(db, index_dir)
|
|
assert index.sync()["mode"] == "rebuild"
|
|
store.add_segments([_segment("seg-2", "학교를 졸업하고 서울로 올라왔다.")])
|
|
result = index.sync()
|
|
assert result == {"mode": "append", "total": 2, "added": 1}
|
|
|
|
|
|
def test_index_returns_provenance_and_evidence(tmp_path):
|
|
db = tmp_path / "corpus.sqlite3"
|
|
store = CorpusStore(db)
|
|
store.upsert_document(DocumentRecord(document_id="doc-1", title="나의 자서전"))
|
|
text = "나는 어린 시절 바닷가 마을에서 살았고 매일 파도 소리를 들었다."
|
|
store.add_segments([_segment("seg-1", text)])
|
|
index = PersistentCorpusIndex(db, tmp_path / "index")
|
|
index.sync()
|
|
hit = index.query("바닷가 마을에서 살았고 매일 파도 소리를 들었다.", top_k=1)[0]
|
|
assert hit.segment_id == "seg-1"
|
|
assert hit.title == "나의 자서전"
|
|
assert hit.source_locator == "book.json#seg-1"
|
|
assert hit.longest_span >= 12
|
|
assert hit.coverage > 0.5
|
|
|
|
|
|
def test_long_query_finds_middle_copy_without_dilution(tmp_path):
|
|
db = tmp_path / "corpus.sqlite3"
|
|
store = CorpusStore(db)
|
|
store.upsert_document(DocumentRecord(document_id="doc-1", title="원본"))
|
|
copied = "바닷가 마을에서 파도 소리를 들으며 자란 기억이 아직도 선명하다. " * 5
|
|
store.add_segments([_segment("seg-1", copied)])
|
|
index = PersistentCorpusIndex(db, tmp_path / "index")
|
|
index.sync()
|
|
query = ("전혀 다른 앞부분입니다. " * 200) + copied + ("다른 뒷부분입니다. " * 200)
|
|
hit = index.query(query, top_k=1)[0]
|
|
assert hit.segment_id == "seg-1"
|
|
assert hit.longest_span >= 100
|
|
assert hit.evidence[0]["start"] > 1000
|
|
|
|
|
|
def test_index_excludes_same_document_and_source_group_before_ranking(tmp_path):
|
|
db = tmp_path / "corpus.sqlite3"
|
|
store = CorpusStore(db)
|
|
copied = "바닷가 마을에서 파도 소리를 들으며 자란 기억이 아직도 선명하다."
|
|
store.upsert_documents([
|
|
DocumentRecord(document_id="doc-self", title="나", metadata={"author_group": "author:a"}),
|
|
DocumentRecord(document_id="doc-same-author", title="같은 작성자", metadata={"source_group": "author:a"}),
|
|
DocumentRecord(document_id="doc-other", title="다른 작성자", metadata={"source_group": "author:b"}),
|
|
])
|
|
store.add_segments([
|
|
SegmentRecord("seg-self", "doc-self", copied, "1"),
|
|
SegmentRecord("seg-same", "doc-same-author", copied, "1"),
|
|
SegmentRecord("seg-other", "doc-other", copied + " 다른 결말입니다.", "1"),
|
|
])
|
|
index = PersistentCorpusIndex(db, tmp_path / "index")
|
|
index.sync()
|
|
|
|
hits = index.query(
|
|
copied, top_k=5,
|
|
exclude_document_ids={"doc-self"},
|
|
exclude_source_groups={"author:a"},
|
|
)
|
|
assert [hit.document_id for hit in hits] == ["doc-other"]
|