fix: harden qdrant response parsing and integration markers
This commit is contained in:
@@ -110,12 +110,16 @@ class QdrantVectorStore:
|
||||
expected_dimension=self._expected_dimension,
|
||||
)
|
||||
|
||||
dimension = info["config"]["params"]["vectors"]["size"]
|
||||
result = self._require_mapping(info, "collection")
|
||||
config = self._require_mapping(result.get("config"), "collection")
|
||||
params = self._require_mapping(config.get("params"), "collection")
|
||||
vectors = self._require_mapping(params.get("vectors"), "collection")
|
||||
dimension = vectors["size"]
|
||||
dimensions = (dimension,)
|
||||
dimension_compatible = (
|
||||
None if self._expected_dimension is None else dimensions == (self._expected_dimension,)
|
||||
)
|
||||
observed_distance = info["config"]["params"]["vectors"].get("distance")
|
||||
observed_distance = vectors["distance"]
|
||||
distance_compatible = (
|
||||
None if self._expected_distance is None else observed_distance == self._expected_distance
|
||||
)
|
||||
@@ -179,9 +183,12 @@ class QdrantVectorStore:
|
||||
"filter": {"must": filter_must},
|
||||
},
|
||||
)
|
||||
points = response.get("result", {}).get("points")
|
||||
result = self._require_mapping(response.get("result"), "query")
|
||||
points = result.get("points")
|
||||
if not isinstance(points, list):
|
||||
raise VectorResponseError("Qdrant returned malformed query response")
|
||||
if not all(isinstance(point, dict) for point in points):
|
||||
raise VectorResponseError("Qdrant returned malformed query response")
|
||||
hits = [self._hit_from_point(point) for point in points]
|
||||
return sorted(hits, key=lambda hit: (-hit.similarity, hit.id))[:limit]
|
||||
|
||||
@@ -380,12 +387,14 @@ class QdrantVectorStore:
|
||||
{"field_name": field_name, "field_schema": "keyword"},
|
||||
)
|
||||
response = self._call("GET", f"/collections/{self._collection}", None)
|
||||
result = response.get("result") if isinstance(response, dict) else None
|
||||
config = result.get("config", {}).get("params", {}).get("vectors") if isinstance(result, dict) else None
|
||||
if not isinstance(config, dict):
|
||||
result = self._require_mapping(response.get("result"), "collection")
|
||||
config = self._require_mapping(result.get("config"), "collection")
|
||||
params = self._require_mapping(config.get("params"), "collection")
|
||||
vectors = self._require_mapping(params.get("vectors"), "collection")
|
||||
size = vectors.get("size")
|
||||
distance = vectors.get("distance")
|
||||
if type(size) is not int or size <= 0 or not isinstance(distance, str):
|
||||
raise VectorResponseError("Qdrant returned malformed collection response")
|
||||
size = config.get("size")
|
||||
distance = config.get("distance")
|
||||
if (
|
||||
self._expected_dimension is not None and size != self._expected_dimension
|
||||
) or (
|
||||
@@ -399,6 +408,11 @@ class QdrantVectorStore:
|
||||
payload_schema = result.get("payload_schema")
|
||||
if not isinstance(payload_schema, dict):
|
||||
raise VectorResponseError("Qdrant returned malformed collection response")
|
||||
if any(
|
||||
not isinstance(field, dict) or not isinstance(field.get("data_type"), str)
|
||||
for field in payload_schema.values()
|
||||
):
|
||||
raise VectorResponseError("Qdrant returned malformed collection response")
|
||||
for field_name in _KEYWORD_INDEXES:
|
||||
field = payload_schema.get(field_name)
|
||||
if not isinstance(field, dict) or field.get("data_type") != "keyword":
|
||||
@@ -437,23 +451,40 @@ class QdrantVectorStore:
|
||||
"Qdrant collection disappeared during semantic index reconciliation"
|
||||
) from exc
|
||||
raise
|
||||
result = response.get("result", {})
|
||||
result = self._require_mapping(response.get("result"), "scroll")
|
||||
page = result.get("points")
|
||||
if not isinstance(page, list):
|
||||
if not isinstance(page, list) or not all(isinstance(point, dict) for point in page):
|
||||
raise VectorResponseError("Qdrant returned malformed scroll response")
|
||||
points.extend(page)
|
||||
next_page_offset = result.get("next_page_offset")
|
||||
if next_page_offset is None:
|
||||
return points
|
||||
if type(next_page_offset) not in (int, str):
|
||||
raise VectorResponseError("Qdrant returned malformed scroll response")
|
||||
if next_page_offset in seen_offsets:
|
||||
raise VectorResponseError("Qdrant returned malformed scroll response")
|
||||
seen_offsets.add(next_page_offset)
|
||||
offset = next_page_offset
|
||||
|
||||
@staticmethod
|
||||
def _require_mapping(value: object, operation: str) -> dict:
|
||||
if not isinstance(value, dict):
|
||||
raise VectorResponseError(f"Qdrant returned malformed {operation} response")
|
||||
return value
|
||||
|
||||
def _hit_from_point(self, point: dict) -> VectorHit:
|
||||
if not isinstance(point, dict):
|
||||
raise VectorResponseError("Qdrant returned malformed query response")
|
||||
payload = point.get("payload")
|
||||
score = point.get("score")
|
||||
if not isinstance(payload, dict) or not isinstance(score, (int, float)):
|
||||
if (
|
||||
not isinstance(payload, dict)
|
||||
or type(score) not in (int, float)
|
||||
or any(
|
||||
key in payload and not isinstance(payload[key], str)
|
||||
for key in ("record_key", "record_kind", "kind", "ref", "title", "content")
|
||||
)
|
||||
):
|
||||
raise VectorResponseError("Qdrant returned malformed query response")
|
||||
return hit_from_metadata(float(score), payload)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user