fix: await ensure_collection and survive partial embed failures in document sync

- ensure_collection was called without await, so the coroutine never ran
  and fresh tenants had no collection when the upsert hit Qdrant
- a single None entry from embed_batch poisoned the point batch and
  aborted the whole document upsert; failed chunks are now skipped with
  a warning (all-failed raises and the IndexResult reports failure)
- raw client.delete/client.upsert calls now go through the async wrapper
  (delete_by_filter and the new batch upsert_points method)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbFZyDvYksazX6nYQYZ67L
This commit is contained in:
2026-07-14 13:00:59 +02:00
co-authored by Claude Fable 5
parent 406143e7ae
commit c35d3c1fa9
4 changed files with 203 additions and 14 deletions
+37
View File
@@ -467,6 +467,43 @@ class QdrantClientWrapper:
logger.error(f"Failed to upsert vector: {e}", exc_info=True)
return False
async def upsert_points(
self,
collection_name: str,
points: List[Dict[str, Any]]
) -> int:
"""
Upsert a batch of vector points in a single request.
Args:
collection_name: Collection name
points: List of {"id": str, "vector": List[float], "payload": dict}
Returns:
Number of points upserted
Raises:
Exception: If the upsert fails (callers decide how to degrade)
"""
if not points:
return 0
structs = [
PointStruct(
id=p["id"],
vector=p["vector"],
payload=p.get("payload", {})
)
for p in points
]
await self.client.upsert(
collection_name=collection_name,
points=structs
)
logger.info(f"Upserted {len(structs)} points into {collection_name}")
return len(structs)
async def delete_by_filter(
self,
collection_name: str,