1515
1616import httpx
1717from django .conf import settings as django_settings
18+ from django .db .models import Model
1819from django .utils .dateparse import parse_datetime
1920from qdrant_client import QdrantClient
2021from qdrant_client .models import (
3435settings = cast (CoreSettings , django_settings )
3536
3637
38+ def _require_pk (instance : Model ) -> int :
39+ """Return a saved model primary key for typed Qdrant payload construction."""
40+
41+ instance_pk = instance .pk
42+ if instance_pk is None :
43+ raise ValueError (f"{ instance .__class__ .__name__ } must be saved first." )
44+ return int (instance_pk )
45+
46+
3747def get_sentence_transformer_class ():
3848 """Lazily import and cache the sentence-transformer class.
3949
@@ -90,7 +100,10 @@ def embed_text(self, text: str) -> list[float]:
90100 def get_embedding_dimension (self ) -> int :
91101 """Return the model's native embedding dimension without probing text."""
92102
93- return int (self .model .get_sentence_embedding_dimension ())
103+ dimension = self .model .get_sentence_embedding_dimension ()
104+ if dimension is None :
105+ raise RuntimeError ("Embedding model did not report a vector dimension." )
106+ return int (dimension )
94107
95108
96109class OllamaEmbeddingProvider (EmbeddingProvider ):
@@ -222,18 +235,20 @@ def upsert_content_embedding(content: Content) -> str:
222235 """
223236
224237 client = get_qdrant_client ()
225- ensure_project_collection (content .project_id )
238+ project_id = _require_pk (content .project )
239+ content_id = _require_pk (content )
240+ ensure_project_collection (project_id )
226241 embedding_id = content .embedding_id or str (uuid4 ())
227242 vector = embed_text (build_content_embedding_text (content ))
228243 client .upsert (
229- collection_name = collection_name_for_project (content . project_id ),
244+ collection_name = collection_name_for_project (project_id ),
230245 points = [
231246 PointStruct (
232247 id = embedding_id ,
233248 vector = vector ,
234249 payload = {
235- "content_id" : content . id ,
236- "project_id" : content . project_id ,
250+ "content_id" : content_id ,
251+ "project_id" : project_id ,
237252 "url" : content .url ,
238253 "title" : content .title ,
239254 "published_date" : serialize_published_date (content .published_date ),
@@ -254,18 +269,20 @@ def upsert_entity_embedding(entity: Entity) -> str:
254269 """Write or update an entity embedding in the project's entity collection."""
255270
256271 client = get_qdrant_client ()
257- ensure_project_entity_collection (entity .project_id )
272+ project_id = _require_pk (entity .project )
273+ entity_id = _require_pk (entity )
274+ ensure_project_entity_collection (project_id )
258275 vector = embed_text (build_entity_embedding_text (entity ))
259- embedding_id = f"entity-{ entity . id } "
276+ embedding_id = f"entity-{ entity_id } "
260277 client .upsert (
261- collection_name = entity_collection_name_for_project (entity . project_id ),
278+ collection_name = entity_collection_name_for_project (project_id ),
262279 points = [
263280 PointStruct (
264281 id = embedding_id ,
265282 vector = vector ,
266283 payload = {
267- "entity_id" : entity . id ,
268- "project_id" : entity . project_id ,
284+ "entity_id" : entity_id ,
285+ "project_id" : project_id ,
269286 "name" : entity .name ,
270287 "type" : entity .type ,
271288 },
@@ -343,11 +360,11 @@ def search_similar_content(
343360 """Find content similar to an existing content row within the same project."""
344361
345362 return search_similar (
346- content .project_id ,
363+ _require_pk ( content .project ) ,
347364 embed_text (build_content_embedding_text (content )),
348365 limit = limit ,
349366 is_reference = is_reference ,
350- exclude_content_id = content . id ,
367+ exclude_content_id = _require_pk ( content ) ,
351368 )
352369
353370
@@ -370,9 +387,10 @@ def search_similar_entities(
370387def search_similar_entities_for_content (content : Content , limit : int = 8 ):
371388 """Find tracked entities whose embeddings are close to a content item."""
372389
373- sync_project_entity_embeddings (content .project_id )
390+ project_id = _require_pk (content .project )
391+ sync_project_entity_embeddings (project_id )
374392 return search_similar_entities (
375- content . project_id ,
393+ project_id ,
376394 embed_text (build_content_embedding_text (content )),
377395 limit = limit ,
378396 )
0 commit comments