mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-05-04 16:52:37 +00:00
fix: make lancedb an optional dependency to fix Windows installation (#5045)
lancedb >=0.30 dropped Windows (win_amd64) wheels, causing 'crewai install' to fail on Windows with: Distribution lancedb==0.30.1 can't be installed because it doesn't have a source distribution or wheel for the current platform Changes: - Move lancedb from core dependencies to [project.optional-dependencies] under a new 'memory-storage' extra - Guard the top-level 'import lancedb' in lancedb_storage.py with try/except ImportError, raising a clear install hint - Guard the lazy import in Memory.model_post_init with a helpful ImportError message pointing to 'pip install crewai[memory-storage]' - Add 3 tests verifying graceful handling when lancedb is absent Co-Authored-By: João <joao@crewai.com>
This commit is contained in:
@@ -43,7 +43,6 @@ dependencies = [
|
||||
"uv~=0.9.13",
|
||||
"aiosqlite~=0.21.0",
|
||||
"pyyaml~=6.0",
|
||||
"lancedb>=0.29.2",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@@ -103,6 +102,9 @@ a2a = [
|
||||
"httpx-sse~=0.4.0",
|
||||
"aiocache[redis,memcached]~=0.12.3",
|
||||
]
|
||||
memory-storage = [
|
||||
"lancedb>=0.29.2",
|
||||
]
|
||||
file-processing = [
|
||||
"crewai-files",
|
||||
]
|
||||
|
||||
@@ -12,12 +12,28 @@ import threading
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
import lancedb # type: ignore[import-untyped]
|
||||
|
||||
try:
|
||||
import lancedb # type: ignore[import-untyped]
|
||||
except ImportError:
|
||||
lancedb = None # type: ignore[assignment]
|
||||
|
||||
from crewai.memory.types import MemoryRecord, ScopeInfo
|
||||
from crewai.utilities.lock_store import lock as store_lock
|
||||
|
||||
|
||||
_INSTALL_HINT = (
|
||||
"lancedb is required for the default memory storage backend but is not installed.\n\n"
|
||||
"Install it with:\n"
|
||||
" pip install crewai[memory-storage] # or: pip install lancedb\n\n"
|
||||
"If lancedb does not provide wheels for your platform (e.g. Windows with "
|
||||
"lancedb >=0.30), pin an older version:\n"
|
||||
" pip install 'lancedb>=0.29.2,<0.30'\n\n"
|
||||
"Alternatively, supply a custom StorageBackend to Memory(storage=...) to "
|
||||
"bypass lancedb entirely."
|
||||
)
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# Default embedding vector dimensionality (matches OpenAI text-embedding-3-small).
|
||||
@@ -63,6 +79,8 @@ class LanceDBStorage:
|
||||
fragment file; compaction merges them, keeping query
|
||||
performance consistent. Set to 0 to disable.
|
||||
"""
|
||||
if lancedb is None:
|
||||
raise ImportError(_INSTALL_HINT)
|
||||
if path is None:
|
||||
storage_dir = os.environ.get("CREWAI_STORAGE_DIR")
|
||||
if storage_dir:
|
||||
|
||||
@@ -173,7 +173,20 @@ class Memory(BaseModel):
|
||||
)
|
||||
|
||||
if isinstance(self.storage, str):
|
||||
from crewai.memory.storage.lancedb_storage import LanceDBStorage
|
||||
try:
|
||||
from crewai.memory.storage.lancedb_storage import LanceDBStorage
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"lancedb is required for the default memory storage backend "
|
||||
"but is not installed.\n\n"
|
||||
"Install it with:\n"
|
||||
" pip install crewai[memory-storage] # or: pip install lancedb\n\n"
|
||||
"If lancedb does not provide wheels for your platform "
|
||||
"(e.g. Windows with lancedb >=0.30), pin an older version:\n"
|
||||
" pip install 'lancedb>=0.29.2,<0.30'\n\n"
|
||||
"Alternatively, supply a custom StorageBackend instance to "
|
||||
"Memory(storage=my_backend) to bypass lancedb entirely."
|
||||
) from exc
|
||||
|
||||
self._storage = (
|
||||
LanceDBStorage()
|
||||
|
||||
@@ -999,3 +999,62 @@ def test_close_drains_and_shuts_down(tmp_path: Path, mock_embedder: MagicMock) -
|
||||
mem.close()
|
||||
# After close, records should be persisted
|
||||
assert mem._storage.count() == 1
|
||||
|
||||
|
||||
# --- lancedb optional dependency ---
|
||||
|
||||
|
||||
def test_lancedb_storage_import_error_when_missing(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""LanceDBStorage.__init__ raises ImportError with install hint when lancedb is absent."""
|
||||
import crewai.memory.storage.lancedb_storage as mod
|
||||
|
||||
# Simulate lancedb not being installed
|
||||
monkeypatch.setattr(mod, "lancedb", None)
|
||||
with pytest.raises(ImportError, match="pip install crewai\\[memory-storage\\]"):
|
||||
mod.LanceDBStorage(path="/tmp/fake")
|
||||
|
||||
|
||||
def test_memory_raises_import_error_when_lancedb_missing(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Memory(storage='lancedb') raises ImportError with install hint when lancedb is absent."""
|
||||
import importlib
|
||||
|
||||
# Make the import of lancedb_storage raise ImportError
|
||||
original_import = importlib.import_module
|
||||
|
||||
def _patched_import(name: str, *args, **kwargs): # type: ignore[no-untyped-def]
|
||||
if name == "crewai.memory.storage.lancedb_storage":
|
||||
raise ImportError("No module named 'lancedb'")
|
||||
return original_import(name, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(importlib, "import_module", _patched_import)
|
||||
|
||||
# Also patch the direct import that model_post_init uses
|
||||
import builtins
|
||||
|
||||
original_builtins_import = builtins.__import__
|
||||
|
||||
def _patched_builtins_import(name, *args, **kwargs): # type: ignore[no-untyped-def]
|
||||
if "lancedb_storage" in name:
|
||||
raise ImportError("No module named 'lancedb'")
|
||||
return original_builtins_import(name, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(builtins, "__import__", _patched_builtins_import)
|
||||
|
||||
from crewai.memory.unified_memory import Memory
|
||||
|
||||
with pytest.raises(ImportError, match="pip install crewai\\[memory-storage\\]"):
|
||||
Memory(storage="lancedb", llm=MagicMock(), embedder=MagicMock())
|
||||
|
||||
|
||||
def test_memory_works_with_custom_storage_when_lancedb_missing(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Memory accepts a custom StorageBackend even when lancedb is not installed."""
|
||||
from crewai.memory.unified_memory import Memory
|
||||
|
||||
mock_storage = MagicMock()
|
||||
# Should not raise even if lancedb is missing — custom storage bypasses it
|
||||
mem = Memory(storage=mock_storage, llm=MagicMock(), embedder=MagicMock())
|
||||
assert mem._storage is mock_storage
|
||||
|
||||
Reference in New Issue
Block a user