mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-04-12 05:52:39 +00:00
Compare commits
3 Commits
1.14.2a2
...
devin/1775
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da4b610d55 | ||
|
|
baac6668ab | ||
|
|
7bec692878 |
@@ -44,7 +44,6 @@ dependencies = [
|
||||
"aiosqlite~=0.21.0",
|
||||
"pyyaml~=6.0",
|
||||
"aiofiles~=24.1.0",
|
||||
"lancedb>=0.29.2,<0.30.1",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@@ -66,6 +65,9 @@ pandas = [
|
||||
openpyxl = [
|
||||
"openpyxl~=3.1.5",
|
||||
]
|
||||
memory = [
|
||||
"lancedb>=0.29.2,<0.30.1",
|
||||
]
|
||||
mem0 = ["mem0ai~=0.1.94"]
|
||||
docling = [
|
||||
"docling~=2.75.0",
|
||||
|
||||
@@ -12,7 +12,10 @@ 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
|
||||
|
||||
from crewai.memory.types import MemoryRecord, ScopeInfo
|
||||
from crewai.utilities.lock_store import lock as store_lock
|
||||
@@ -63,6 +66,12 @@ class LanceDBStorage:
|
||||
fragment file; compaction merges them, keeping query
|
||||
performance consistent. Set to 0 to disable.
|
||||
"""
|
||||
if lancedb is None:
|
||||
raise ImportError(
|
||||
"lancedb is required for LanceDB memory storage but is not installed.\n"
|
||||
"Install it with: pip install 'crewai[memory]'\n"
|
||||
"Or directly: pip install 'lancedb>=0.29.2,<0.30.1'"
|
||||
)
|
||||
if path is None:
|
||||
storage_dir = os.environ.get("CREWAI_STORAGE_DIR")
|
||||
if storage_dir:
|
||||
|
||||
140
lib/crewai/tests/memory/test_lancedb_optional.py
Normal file
140
lib/crewai/tests/memory/test_lancedb_optional.py
Normal file
@@ -0,0 +1,140 @@
|
||||
"""Tests that lancedb is an optional dependency.
|
||||
|
||||
These tests verify that:
|
||||
1. The lancedb_storage module handles a missing lancedb gracefully.
|
||||
2. Memory falls back with a clear error when lancedb is not installed.
|
||||
3. Importing crewai itself does not require lancedb.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_lancedb_storage_raises_import_error_when_lancedb_missing(tmp_path):
|
||||
"""LanceDBStorage.__init__ raises ImportError with install instructions when lancedb is absent."""
|
||||
with patch.dict(sys.modules, {"lancedb": None}):
|
||||
# Force reload so the module picks up the patched sys.modules
|
||||
import importlib
|
||||
|
||||
import crewai.memory.storage.lancedb_storage as mod
|
||||
|
||||
importlib.reload(mod)
|
||||
|
||||
with pytest.raises(ImportError, match="pip install 'crewai\\[memory\\]'"):
|
||||
mod.LanceDBStorage(path=str(tmp_path / "mem"))
|
||||
|
||||
# Restore the module to its original state
|
||||
importlib.reload(mod)
|
||||
|
||||
|
||||
def test_memory_default_storage_raises_when_lancedb_missing(tmp_path):
|
||||
"""Memory(storage='lancedb') raises ImportError when lancedb is not installed."""
|
||||
with patch.dict(sys.modules, {"lancedb": None}):
|
||||
import importlib
|
||||
|
||||
import crewai.memory.storage.lancedb_storage as mod
|
||||
|
||||
importlib.reload(mod)
|
||||
|
||||
try:
|
||||
from crewai.memory.unified_memory import Memory
|
||||
|
||||
with pytest.raises(ImportError, match="pip install 'crewai\\[memory\\]'"):
|
||||
Memory(
|
||||
storage="lancedb",
|
||||
llm=MagicMock(),
|
||||
embedder=MagicMock(),
|
||||
)
|
||||
finally:
|
||||
importlib.reload(mod)
|
||||
|
||||
|
||||
def test_memory_with_path_string_raises_when_lancedb_missing(tmp_path):
|
||||
"""Memory(storage='/some/path') also uses LanceDBStorage and raises when lancedb is missing."""
|
||||
with patch.dict(sys.modules, {"lancedb": None}):
|
||||
import importlib
|
||||
|
||||
import crewai.memory.storage.lancedb_storage as mod
|
||||
|
||||
importlib.reload(mod)
|
||||
|
||||
try:
|
||||
from crewai.memory.unified_memory import Memory
|
||||
|
||||
with pytest.raises(ImportError, match="pip install 'crewai\\[memory\\]'"):
|
||||
Memory(
|
||||
storage=str(tmp_path / "custom_path"),
|
||||
llm=MagicMock(),
|
||||
embedder=MagicMock(),
|
||||
)
|
||||
finally:
|
||||
importlib.reload(mod)
|
||||
|
||||
|
||||
def test_crewai_import_does_not_require_lancedb():
|
||||
"""Importing crewai should work even if lancedb is not installed.
|
||||
|
||||
The Memory class is lazily imported in crewai/__init__.py, so lancedb
|
||||
should never be pulled in at import time.
|
||||
"""
|
||||
# This test verifies the lazy import mechanism by checking that the
|
||||
# crewai module is importable and that Memory is listed in __all__
|
||||
# but not yet resolved in the module globals until accessed.
|
||||
import crewai
|
||||
|
||||
assert "Memory" in crewai.__all__
|
||||
# Memory should be accessible (lazy import triggers on access)
|
||||
assert hasattr(crewai, "Memory")
|
||||
|
||||
|
||||
def test_memory_with_custom_storage_backend_does_not_need_lancedb(tmp_path):
|
||||
"""When a custom StorageBackend is passed, lancedb is never needed."""
|
||||
with patch.dict(sys.modules, {"lancedb": None}):
|
||||
import importlib
|
||||
|
||||
import crewai.memory.storage.lancedb_storage as mod
|
||||
|
||||
importlib.reload(mod)
|
||||
|
||||
try:
|
||||
from crewai.memory.unified_memory import Memory
|
||||
|
||||
mock_storage = MagicMock()
|
||||
# Should not raise, since we're providing a custom storage backend
|
||||
mem = Memory(
|
||||
storage=mock_storage,
|
||||
llm=MagicMock(),
|
||||
embedder=MagicMock(),
|
||||
)
|
||||
assert mem._storage is mock_storage
|
||||
finally:
|
||||
importlib.reload(mod)
|
||||
|
||||
|
||||
def test_lancedb_in_optional_dependencies():
|
||||
"""Verify lancedb is listed under optional [memory] dependencies, not core."""
|
||||
import tomli
|
||||
from pathlib import Path
|
||||
|
||||
pyproject_path = Path(__file__).resolve().parents[2] / "pyproject.toml"
|
||||
with open(pyproject_path, "rb") as f:
|
||||
data = tomli.load(f)
|
||||
|
||||
core_deps = data["project"]["dependencies"]
|
||||
optional_deps = data["project"]["optional-dependencies"]
|
||||
|
||||
# lancedb should NOT be in core dependencies
|
||||
assert not any("lancedb" in dep for dep in core_deps), (
|
||||
"lancedb should not be a core dependency"
|
||||
)
|
||||
|
||||
# lancedb SHOULD be in optional [memory] dependencies
|
||||
assert "memory" in optional_deps, "Missing [memory] optional dependency group"
|
||||
memory_deps = optional_deps["memory"]
|
||||
assert any("lancedb" in dep for dep in memory_deps), (
|
||||
"lancedb should be in the [memory] optional dependency group"
|
||||
)
|
||||
10
uv.lock
generated
10
uv.lock
generated
@@ -13,7 +13,7 @@ resolution-markers = [
|
||||
]
|
||||
|
||||
[options]
|
||||
exclude-newer = "2026-04-04T15:11:41.651093Z"
|
||||
exclude-newer = "2026-04-04T21:52:55.380648312Z"
|
||||
exclude-newer-span = "P3D"
|
||||
|
||||
[manifest]
|
||||
@@ -1209,7 +1209,6 @@ dependencies = [
|
||||
{ name = "json-repair" },
|
||||
{ name = "json5" },
|
||||
{ name = "jsonref" },
|
||||
{ name = "lancedb" },
|
||||
{ name = "mcp" },
|
||||
{ name = "openai" },
|
||||
{ name = "openpyxl" },
|
||||
@@ -1269,6 +1268,9 @@ litellm = [
|
||||
mem0 = [
|
||||
{ name = "mem0ai" },
|
||||
]
|
||||
memory = [
|
||||
{ name = "lancedb" },
|
||||
]
|
||||
openpyxl = [
|
||||
{ name = "openpyxl" },
|
||||
]
|
||||
@@ -1317,7 +1319,7 @@ requires-dist = [
|
||||
{ name = "json-repair", specifier = "~=0.25.2" },
|
||||
{ name = "json5", specifier = "~=0.10.0" },
|
||||
{ name = "jsonref", specifier = "~=1.1.0" },
|
||||
{ name = "lancedb", specifier = ">=0.29.2,<0.30.1" },
|
||||
{ name = "lancedb", marker = "extra == 'memory'", specifier = ">=0.29.2,<0.30.1" },
|
||||
{ name = "litellm", marker = "extra == 'litellm'", specifier = "~=1.83.0" },
|
||||
{ name = "mcp", specifier = "~=1.26.0" },
|
||||
{ name = "mem0ai", marker = "extra == 'mem0'", specifier = "~=0.1.94" },
|
||||
@@ -1346,7 +1348,7 @@ requires-dist = [
|
||||
{ name = "uv", specifier = "~=0.9.13" },
|
||||
{ name = "voyageai", marker = "extra == 'voyageai'", specifier = "~=0.3.5" },
|
||||
]
|
||||
provides-extras = ["a2a", "anthropic", "aws", "azure-ai-inference", "bedrock", "docling", "embeddings", "file-processing", "google-genai", "litellm", "mem0", "openpyxl", "pandas", "qdrant", "qdrant-edge", "tools", "voyageai", "watson"]
|
||||
provides-extras = ["a2a", "anthropic", "aws", "azure-ai-inference", "bedrock", "docling", "embeddings", "file-processing", "google-genai", "litellm", "mem0", "memory", "openpyxl", "pandas", "qdrant", "qdrant-edge", "tools", "voyageai", "watson"]
|
||||
|
||||
[[package]]
|
||||
name = "crewai-devtools"
|
||||
|
||||
Reference in New Issue
Block a user