diff --git a/mem0/vector_stores/qdrant.py b/mem0/vector_stores/qdrant.py index 3f29edc4b..99cca10d1 100644 --- a/mem0/vector_stores/qdrant.py +++ b/mem0/vector_stores/qdrant.py @@ -98,7 +98,10 @@ class Qdrant(VectorStoreBase): self._bm25_encoder = SparseTextEmbedding(model_name="Qdrant/bm25") logger.info("BM25 encoder loaded (fastembed Qdrant/bm25)") except ImportError: - logger.warning("fastembed not installed — BM25 keyword search disabled. Install with: pip install fastembed") + logger.warning( + "fastembed not installed - BM25 keyword search disabled. " + 'Install it with: pip install "mem0ai[extras]"' + ) self._bm25_encoder = False # sentinel: tried and failed except Exception as e: logger.warning(f"Failed to load BM25 encoder: {e}") diff --git a/tests/vector_stores/test_qdrant.py b/tests/vector_stores/test_qdrant.py index 3bee29052..d647abf6c 100644 --- a/tests/vector_stores/test_qdrant.py +++ b/tests/vector_stores/test_qdrant.py @@ -1025,3 +1025,19 @@ class TestQdrantDatetimeRangeFilters(unittest.TestCase): types = {type(c.range) for c in result.must} self.assertIn(DatetimeRange, types) self.assertIn(Range, types) + + def test_missing_fastembed_warns_with_extras_install_hint(self): + """When fastembed is missing, the BM25 warning must point at the mem0 optional group + that actually provides it (`mem0ai[extras]`), not the bare `fastembed` package - + fastembed is declared under [extras], so that is the discoverable install path.""" + self.qdrant._bm25_encoder = None + # Setting the module to None in sys.modules makes `from fastembed import ...` raise ImportError. + with patch.dict("sys.modules", {"fastembed": None}): + with self.assertLogs("mem0.vector_stores.qdrant", level="WARNING") as cm: + result = self.qdrant._get_bm25_encoder() + self.assertIsNone(result) # encoder unavailable -> None + self.assertIs(self.qdrant._bm25_encoder, False) # sentinel: tried and failed, do not retry + joined = "\n".join(cm.output) + self.assertIn("mem0ai[extras]", joined) # points at the right install + self.assertNotIn("pip install fastembed", joined) # not the bare package + self.assertNotIn("\u2014", joined) # no em-dash