fix(evidence): fail closed when BM25 is unavailable
This commit is contained in:
@@ -32,6 +32,7 @@ class FakeQdrantHttp:
|
||||
self.dimension = dimension
|
||||
self.distance = distance
|
||||
self.collection = None
|
||||
self.sparse_vectors: dict[str, dict] | None = None
|
||||
self.payload_indexes: set[str] = set()
|
||||
self.points: dict[str, dict] = {}
|
||||
self.calls: list[tuple[str, str, dict | None]] = []
|
||||
@@ -56,7 +57,11 @@ class FakeQdrantHttp:
|
||||
"result": {
|
||||
"config": {
|
||||
"params": {
|
||||
"vectors": {"size": self.dimension, "distance": self.distance}
|
||||
"vectors": {"size": self.dimension, "distance": self.distance},
|
||||
**(
|
||||
{"sparse_vectors": self.sparse_vectors}
|
||||
if self.sparse_vectors is not None else {}
|
||||
),
|
||||
}
|
||||
},
|
||||
"payload_schema": {
|
||||
@@ -69,6 +74,7 @@ class FakeQdrantHttp:
|
||||
self.collection = json
|
||||
self.dimension = json["vectors"]["size"]
|
||||
self.distance = json["vectors"]["distance"]
|
||||
self.sparse_vectors = json.get("sparse_vectors")
|
||||
return FakeResponse(200, {"status": "ok"})
|
||||
|
||||
if method == "PUT" and path == "/collections/workspace-semantic/index":
|
||||
@@ -182,6 +188,15 @@ def _store(
|
||||
)
|
||||
|
||||
|
||||
def _ready_collection_with_bm25(fake: FakeQdrantHttp) -> None:
|
||||
fake.collection = {"vectors": {"size": 1024, "distance": "Cosine"}}
|
||||
fake.payload_indexes = {
|
||||
"content_hash", "document_id", "kind", "record_key", "record_kind",
|
||||
"vector_generation", "workspace_id", "workspace_revision",
|
||||
}
|
||||
fake.sparse_vectors = {"bm25": {"modifier": "idf"}}
|
||||
|
||||
|
||||
def test_point_id_is_deterministic_uuidv5():
|
||||
assert point_id("demo", "memory", "memory:1") == str(
|
||||
uuid5(NAMESPACE_URL, "thothii:demo:memory:memory:1")
|
||||
@@ -324,6 +339,20 @@ def test_health_fails_when_required_payload_indexes_are_missing_without_creating
|
||||
assert fake.payload_indexes == set()
|
||||
|
||||
|
||||
def test_health_reports_when_evidence_bm25_is_unavailable_without_disabling_dense_callers():
|
||||
fake = FakeQdrantHttp()
|
||||
fake.collection = {"vectors": {"size": 1024, "distance": "Cosine"}}
|
||||
fake.payload_indexes = {
|
||||
"content_hash", "document_id", "kind", "record_key", "record_kind",
|
||||
"vector_generation", "workspace_id", "workspace_revision",
|
||||
}
|
||||
|
||||
health = _store(fake).health()
|
||||
|
||||
assert health.ok is True
|
||||
assert health.bm25_compatible is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("record", "semantic_kind"),
|
||||
[
|
||||
@@ -363,6 +392,7 @@ def test_upsert_serializes_qdrant_point_payloads(record, semantic_kind):
|
||||
|
||||
def test_evidence_upsert_sends_dense_and_server_side_italian_bm25():
|
||||
fake = FakeQdrantHttp()
|
||||
_ready_collection_with_bm25(fake)
|
||||
store = _store(fake)
|
||||
record = _write_record(
|
||||
"demo:gen:11111111111111111111111111111111:chunk:1",
|
||||
@@ -391,6 +421,7 @@ def test_evidence_upsert_sends_dense_and_server_side_italian_bm25():
|
||||
|
||||
def test_evidence_search_uses_filtered_dense_and_bm25_prefetches_with_default_rrf():
|
||||
fake = FakeQdrantHttp()
|
||||
_ready_collection_with_bm25(fake)
|
||||
store = _store(fake)
|
||||
generation = "gen:" + "1" * 32
|
||||
store.upsert("evidence", [
|
||||
@@ -477,6 +508,19 @@ def test_evidence_search_refuses_dense_only_fallback():
|
||||
store.search(["evidence"], [0.2] * 1024, limit=5, kinds=["evidence"])
|
||||
|
||||
|
||||
def test_hybrid_evidence_search_refuses_a_collection_without_bm25():
|
||||
fake = FakeQdrantHttp()
|
||||
_ready_collection_with_bm25(fake)
|
||||
fake.sparse_vectors = None
|
||||
store = _store(fake)
|
||||
|
||||
with pytest.raises(VectorStoreError, match="BM25 collection configuration"):
|
||||
store.search(
|
||||
["evidence"], [0.2] * 1024, limit=5, kinds=["evidence"],
|
||||
query_text="cardiomiopatia", query_language="italian",
|
||||
)
|
||||
|
||||
|
||||
def test_search_excludes_inconsistent_semantic_kind_in_bound_workspace():
|
||||
fake = FakeQdrantHttp()
|
||||
store = _store(fake)
|
||||
|
||||
Reference in New Issue
Block a user