feat(evidence): add safe retention and materialized reads
This commit is contained in:
@@ -84,16 +84,64 @@ def item(name, fingerprint):
|
||||
)
|
||||
|
||||
|
||||
def pipeline(tmp_path, source, *, embedder=None, vectors=None, model="model-a", policy=None):
|
||||
def pipeline(tmp_path, source, *, embedder=None, vectors=None, model="model-a", policy=None,
|
||||
retain=3):
|
||||
return CorpusPipeline(
|
||||
store=CorpusStore(tmp_path / "corpus"), sources=[source],
|
||||
embedder=embedder or Embedder(), vector_store=vectors or Vectors(),
|
||||
embedding_model=model, embedding_dimensions=3,
|
||||
chunk_policy=policy or ChunkPolicy(version="chunk-v1", max_chars=100),
|
||||
pipeline_version="evidence-v1",
|
||||
retain_published_generations=retain,
|
||||
)
|
||||
|
||||
|
||||
def test_retention_bounds_generations_and_purges_vectors_after_publish(tmp_path):
|
||||
vectors = Vectors()
|
||||
generations = []
|
||||
for index in range(4):
|
||||
result = pipeline(
|
||||
tmp_path, Source([(item("one", str(index)), f"version {index}")]),
|
||||
vectors=vectors, retain=2,
|
||||
).run_as_job(
|
||||
workspace_id="demo", workspace_root=tmp_path,
|
||||
config_fingerprint="sha256:" + "1" * 64,
|
||||
input_fingerprint="sha256:" + str(index) * 64,
|
||||
)
|
||||
generations.append(result.generation)
|
||||
store = CorpusStore(tmp_path / "corpus")
|
||||
assert store.list_generations() == generations[-2:]
|
||||
assert {r.record.metadata["vector_generation"] for r in vectors.records} == set(generations[-2:])
|
||||
assert store.active_generation() == generations[-1]
|
||||
|
||||
|
||||
def test_retention_keeps_filesystem_when_vector_purge_fails_then_retries(tmp_path):
|
||||
class FailingDelete(Vectors):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.fail_delete = True
|
||||
|
||||
def delete_generation(self, collection, generation):
|
||||
if self.fail_delete:
|
||||
raise RuntimeError("credential secret")
|
||||
return super().delete_generation(collection, generation)
|
||||
|
||||
vectors = FailingDelete()
|
||||
for index in range(2):
|
||||
pipeline(tmp_path, Source([(item("one", str(index)), str(index))]), vectors=vectors,
|
||||
retain=1).run_as_job(
|
||||
workspace_id="demo", workspace_root=tmp_path,
|
||||
config_fingerprint="sha256:" + "1" * 64,
|
||||
input_fingerprint="sha256:" + str(index) * 64,
|
||||
)
|
||||
assert len(CorpusStore(tmp_path / "corpus").list_generations()) == 2
|
||||
vectors.fail_delete = False
|
||||
report = pipeline(tmp_path, Source([(item("one", "1"), "1")]), vectors=vectors,
|
||||
retain=1).gc(workspace_root=tmp_path)
|
||||
assert report["status"] == "succeeded"
|
||||
assert len(CorpusStore(tmp_path / "corpus").list_generations()) == 1
|
||||
|
||||
|
||||
def test_unchanged_documents_skip_acquire_normalize_chunk_and_embed(tmp_path):
|
||||
one = item("one", "a")
|
||||
first_source = Source([(one, "hello")])
|
||||
|
||||
Reference in New Issue
Block a user