diff --git a/mnemosyne/library/services/search.py b/mnemosyne/library/services/search.py index c4d4ea0..234ce39 100644 --- a/mnemosyne/library/services/search.py +++ b/mnemosyne/library/services/search.py @@ -60,6 +60,7 @@ class SearchRequest: query: str query_image: Optional[bytes] = None + query_image_ext: str = "png" library_uid: Optional[str] = None library_type: Optional[str] = None collection_uid: Optional[str] = None @@ -263,6 +264,31 @@ class SearchService: try: client = EmbeddingClient(embedding_model, user=self.user) + + # Prefer image embedding when an image is supplied AND the system + # model supports multimodal. Text still flows through fulltext + # search and the reranker independently. + if request.query_image and embedding_model.supports_multimodal: + vector = client.embed_image( + request.query_image, image_ext=request.query_image_ext + ) + if vector is not None: + logger.debug( + "Query embedded from image dimensions=%d ext=%s", + len(vector), + request.query_image_ext, + ) + return vector + logger.warning( + "Image embedding returned None — falling back to text query" + ) + elif request.query_image: + logger.warning( + "query_image supplied but model %s lacks supports_multimodal " + "— falling back to text", + embedding_model.name, + ) + vector = client.embed_text(query_text) logger.debug( "Query embedded dimensions=%d instruction_len=%d", diff --git a/mnemosyne/library/templates/library/library_detail.html b/mnemosyne/library/templates/library/library_detail.html index 59de668..1991d39 100644 --- a/mnemosyne/library/templates/library/library_detail.html +++ b/mnemosyne/library/templates/library/library_detail.html @@ -49,6 +49,179 @@ + +
+ Runs the query twice — once without the re-ranker, once with — so you can see what the re-ranker changed. +
+ + + + {% if search_error %} +{{ candidate.text_preview }}
+{{ candidate.text_preview }}
+{{ image.description }}
+