2121from typing import Any , Iterable
2222
2323from qdrant_client import QdrantClient , models
24- from agno .knowledge .document import Document
2524
2625from app .infrastructure .utils .migration_context import migration_name_ctx
2726from app .infrastructure .utils .user_context import current_user
@@ -78,6 +77,25 @@ def _collection_name(scope: str) -> str:
7877 return f"{ QDRANT_COLLECTION_PREFIX } -{ digest } -{ safe_scope } "
7978
8079
80+ class QdrantRecord (dict ):
81+ """Hybrid Qdrant record compatible with both dict-style and Agno Document-style access.
82+
83+ LegacyLens has two existing access patterns: some helpers index records as
84+ dictionaries while planning/conversion helpers access ``record.meta_data``
85+ and ``record.content``. A plain dict or plain Agno Document cannot satisfy
86+ both contracts, so this adapter intentionally supports both.
87+ """
88+
89+ def __init__ (self , payload : dict [str , Any ], * , score : float | None = None ):
90+ super ().__init__ (payload )
91+ self .id = str (payload .get ("id" ) or "" )
92+ self .name = str (payload .get ("name" ) or "" )
93+ self .content = str (payload .get ("text_content" ) or payload .get ("content" ) or "" )
94+ metadata = payload .get ("metadata" )
95+ self .meta_data = dict (metadata ) if isinstance (metadata , dict ) else {}
96+ self .score = score
97+
98+
8199class QdrantKnowledgeBase :
82100 """Small sync knowledge-base facade used by the existing agent code."""
83101
@@ -246,31 +264,9 @@ def insert_many(self, contents: Iterable[dict[str, Any]]) -> int:
246264 return len (points )
247265
248266 @staticmethod
249- def _as_document (payload : dict [str , Any ], * , score : float | None = None ) -> Document :
250- """Convert a Qdrant payload into the Agno Document contract expected by LegacyLens.
251-
252- The original LanceDB/Agno path returned Document instances. Keeping that
253- contract here avoids leaking Qdrant payload dictionaries into existing
254- planning/conversion code that accesses ``doc.content`` and
255- ``doc.meta_data`` attributes.
256- """
257- metadata = payload .get ("metadata" )
258- if not isinstance (metadata , dict ):
259- metadata = {}
260- doc = Document (
261- id = str (payload .get ("id" ) or "" ),
262- name = str (payload .get ("name" ) or "" ),
263- content = str (payload .get ("text_content" ) or payload .get ("content" ) or "" ),
264- meta_data = metadata ,
265- )
266- if score is not None :
267- # Agno versions used by LegacyLens expose score on search results;
268- # set it defensively so this adapter remains compatible across versions.
269- try :
270- doc .score = score
271- except Exception :
272- pass
273- return doc
267+ def _as_document (payload : dict [str , Any ], * , score : float | None = None ) -> QdrantRecord :
268+ """Return a hybrid record preserving both LegacyLens result contracts."""
269+ return QdrantRecord (payload , score = score )
274270
275271 def search (
276272 self ,
@@ -280,7 +276,7 @@ def search(
280276 limit : int | None = None ,
281277 max_results : int | None = None ,
282278 ** _ : Any ,
283- ) -> list [dict [ str , Any ] ]:
279+ ) -> list [QdrantRecord ]:
284280 top_k = int (max_results or limit or QDRANT_TOP_K )
285281 if not query :
286282 return []
@@ -311,22 +307,22 @@ def search(
311307 with_payload = True ,
312308 )
313309
314- results : list [Document ] = []
310+ results : list [QdrantRecord ] = []
315311 for point in response .points :
316312 payload = point .payload or {}
317313 doc = self ._as_document (payload , score = float (point .score or 0.0 ))
318314 results .append (doc )
319315 return results
320316
321- def scroll (self , * , filters : dict [str , Any ] | None = None , limit : int = 1000 ) -> list [dict [ str , Any ] ]:
317+ def scroll (self , * , filters : dict [str , Any ] | None = None , limit : int = 1000 ) -> list [QdrantRecord ]:
322318 records , _ = self .client .scroll (
323319 collection_name = self .collection_name ,
324320 scroll_filter = self ._scoped_filter (filters ),
325321 limit = limit ,
326322 with_payload = True ,
327323 with_vectors = False ,
328324 )
329- return [record .payload or {} for record in records ]
325+ return [QdrantRecord ( record .payload or {}) for record in records ]
330326
331327 def delete (self , * , filters : dict [str , Any ] | None = None ) -> None :
332328 self .client .delete (
0 commit comments