Fix NumPy 2.x compatibility issue (#2431)

Co-Authored-By: Joe Moura <joao@crewai.com>
This commit is contained in:
Devin AI
2025-03-21 05:10:53 +00:00
parent 03f1d57463
commit 486cf58c3b
4 changed files with 442 additions and 199 deletions

View File

@@ -4,13 +4,26 @@ import io
import logging import logging
import os import os
import shutil import shutil
import warnings
from typing import Any, Dict, List, Optional, Union, cast from typing import Any, Dict, List, Optional, Union, cast
# Initialize with None to indicate module import status
chromadb = None
ClientAPI = None
OneOrMany = None
Settings = None
CHROMADB_AVAILABLE = False
# Try to import chromadb-related modules with proper error handling
try:
import chromadb import chromadb
import chromadb.errors import chromadb.errors
from chromadb.api import ClientAPI from chromadb.api import ClientAPI
from chromadb.api.types import OneOrMany from chromadb.api.types import OneOrMany
from chromadb.config import Settings from chromadb.config import Settings
CHROMADB_AVAILABLE = True
except (ImportError, AttributeError) as e:
warnings.warn(f"Failed to import chromadb: {str(e)}. Knowledge functionality will be limited.")
from crewai.knowledge.storage.base_knowledge_storage import BaseKnowledgeStorage from crewai.knowledge.storage.base_knowledge_storage import BaseKnowledgeStorage
from crewai.utilities import EmbeddingConfigurator from crewai.utilities import EmbeddingConfigurator
@@ -42,9 +55,9 @@ class KnowledgeStorage(BaseKnowledgeStorage):
search efficiency. search efficiency.
""" """
collection: Optional[chromadb.Collection] = None collection = None # Type annotation removed to handle case when chromadb is not available
collection_name: Optional[str] = "knowledge" collection_name: Optional[str] = "knowledge"
app: Optional[ClientAPI] = None app = None # Type annotation removed to handle case when chromadb is not available
def __init__( def __init__(
self, self,
@@ -61,8 +74,13 @@ class KnowledgeStorage(BaseKnowledgeStorage):
filter: Optional[dict] = None, filter: Optional[dict] = None,
score_threshold: float = 0.35, score_threshold: float = 0.35,
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
if not CHROMADB_AVAILABLE:
logging.warning("Cannot search knowledge as chromadb is not available.")
return []
with suppress_logging(): with suppress_logging():
if self.collection: if self.collection:
try:
fetched = self.collection.query( fetched = self.collection.query(
query_texts=query, query_texts=query,
n_results=limit, n_results=limit,
@@ -79,10 +97,21 @@ class KnowledgeStorage(BaseKnowledgeStorage):
if result["score"] >= score_threshold: if result["score"] >= score_threshold:
results.append(result) results.append(result)
return results return results
except Exception as e:
logging.error(f"Error during knowledge search: {str(e)}")
return []
else: else:
raise Exception("Collection not initialized") logging.warning("Collection not initialized")
return []
def initialize_knowledge_storage(self): def initialize_knowledge_storage(self):
if not CHROMADB_AVAILABLE:
logging.warning("Cannot initialize knowledge storage as chromadb is not available.")
self.app = None
self.collection = None
return
try:
base_path = os.path.join(db_storage_path(), "knowledge") base_path = os.path.join(db_storage_path(), "knowledge")
chroma_client = chromadb.PersistentClient( chroma_client = chromadb.PersistentClient(
path=base_path, path=base_path,
@@ -91,7 +120,6 @@ class KnowledgeStorage(BaseKnowledgeStorage):
self.app = chroma_client self.app = chroma_client
try:
collection_name = ( collection_name = (
f"knowledge_{self.collection_name}" f"knowledge_{self.collection_name}"
if self.collection_name if self.collection_name
@@ -102,11 +130,19 @@ class KnowledgeStorage(BaseKnowledgeStorage):
name=collection_name, embedding_function=self.embedder name=collection_name, embedding_function=self.embedder
) )
else: else:
raise Exception("Vector Database Client not initialized") logging.warning("Vector Database Client not initialized")
except Exception: self.collection = None
raise Exception("Failed to create or get collection") except Exception as e:
logging.error(f"Failed to create or get collection: {str(e)}")
self.app = None
self.collection = None
def reset(self): def reset(self):
if not CHROMADB_AVAILABLE:
logging.warning("Cannot reset knowledge storage as chromadb is not available.")
return
try:
base_path = os.path.join(db_storage_path(), KNOWLEDGE_DIRECTORY) base_path = os.path.join(db_storage_path(), KNOWLEDGE_DIRECTORY)
if not self.app: if not self.app:
self.app = chromadb.PersistentClient( self.app = chromadb.PersistentClient(
@@ -116,6 +152,9 @@ class KnowledgeStorage(BaseKnowledgeStorage):
self.app.reset() self.app.reset()
shutil.rmtree(base_path) shutil.rmtree(base_path)
except Exception as e:
logging.error(f"Error during knowledge reset: {str(e)}")
finally:
self.app = None self.app = None
self.collection = None self.collection = None
@@ -124,8 +163,13 @@ class KnowledgeStorage(BaseKnowledgeStorage):
documents: List[str], documents: List[str],
metadata: Optional[Union[Dict[str, Any], List[Dict[str, Any]]]] = None, metadata: Optional[Union[Dict[str, Any], List[Dict[str, Any]]]] = None,
): ):
if not CHROMADB_AVAILABLE:
logging.warning("Cannot save to knowledge storage as chromadb is not available.")
return
if not self.collection: if not self.collection:
raise Exception("Collection not initialized") logging.warning("Collection not initialized")
return
try: try:
# Create a dictionary to store unique documents # Create a dictionary to store unique documents
@@ -154,31 +198,36 @@ class KnowledgeStorage(BaseKnowledgeStorage):
filtered_ids.append(doc_id) filtered_ids.append(doc_id)
# If we have no metadata at all, set it to None # If we have no metadata at all, set it to None
final_metadata: Optional[OneOrMany[chromadb.Metadata]] = ( final_metadata = None
None if all(m is None for m in filtered_metadata) else filtered_metadata if not all(m is None for m in filtered_metadata):
) final_metadata = filtered_metadata
self.collection.upsert( self.collection.upsert(
documents=filtered_docs, documents=filtered_docs,
metadatas=final_metadata, metadatas=final_metadata,
ids=filtered_ids, ids=filtered_ids,
) )
except chromadb.errors.InvalidDimensionException as e: except Exception as e:
if hasattr(chromadb, 'errors') and isinstance(e, chromadb.errors.InvalidDimensionException):
Logger(verbose=True).log( Logger(verbose=True).log(
"error", "error",
"Embedding dimension mismatch. This usually happens when mixing different embedding models. Try resetting the collection using `crewai reset-memories -a`", "Embedding dimension mismatch. This usually happens when mixing different embedding models. Try resetting the collection using `crewai reset-memories -a`",
"red", "red",
) )
raise ValueError( logging.error(
"Embedding dimension mismatch. Make sure you're using the same embedding model " "Embedding dimension mismatch. Make sure you're using the same embedding model "
"across all operations with this collection." "across all operations with this collection."
"Try resetting the collection using `crewai reset-memories -a`" "Try resetting the collection using `crewai reset-memories -a`"
) from e )
except Exception as e: else:
Logger(verbose=True).log("error", f"Failed to upsert documents: {e}", "red") Logger(verbose=True).log("error", f"Failed to upsert documents: {e}", "red")
raise logging.error(f"Failed to upsert documents: {e}")
def _create_default_embedding_function(self): def _create_default_embedding_function(self):
if not CHROMADB_AVAILABLE:
return None
try:
from chromadb.utils.embedding_functions.openai_embedding_function import ( from chromadb.utils.embedding_functions.openai_embedding_function import (
OpenAIEmbeddingFunction, OpenAIEmbeddingFunction,
) )
@@ -186,6 +235,9 @@ class KnowledgeStorage(BaseKnowledgeStorage):
return OpenAIEmbeddingFunction( return OpenAIEmbeddingFunction(
api_key=os.getenv("OPENAI_API_KEY"), model_name="text-embedding-3-small" api_key=os.getenv("OPENAI_API_KEY"), model_name="text-embedding-3-small"
) )
except (ImportError, AttributeError) as e:
logging.warning(f"Failed to create default embedding function: {str(e)}")
return None
def _set_embedder_config(self, embedder: Optional[Dict[str, Any]] = None) -> None: def _set_embedder_config(self, embedder: Optional[Dict[str, Any]] = None) -> None:
"""Set the embedding configuration for the knowledge storage. """Set the embedding configuration for the knowledge storage.
@@ -194,8 +246,12 @@ class KnowledgeStorage(BaseKnowledgeStorage):
embedder_config (Optional[Dict[str, Any]]): Configuration dictionary for the embedder. embedder_config (Optional[Dict[str, Any]]): Configuration dictionary for the embedder.
If None or empty, defaults to the default embedding function. If None or empty, defaults to the default embedding function.
""" """
try:
self.embedder = ( self.embedder = (
EmbeddingConfigurator().configure_embedder(embedder) EmbeddingConfigurator().configure_embedder(embedder)
if embedder if embedder
else self._create_default_embedding_function() else self._create_default_embedding_function()
) )
except Exception as e:
logging.warning(f"Failed to configure embedder: {str(e)}")
self.embedder = None

View File

@@ -60,6 +60,7 @@ class RAGStorage(BaseRAGStorage):
self.embedder_config = configurator.configure_embedder(self.embedder_config) self.embedder_config = configurator.configure_embedder(self.embedder_config)
def _initialize_app(self): def _initialize_app(self):
try:
import chromadb import chromadb
from chromadb.config import Settings from chromadb.config import Settings
@@ -79,6 +80,11 @@ class RAGStorage(BaseRAGStorage):
self.collection = self.app.create_collection( self.collection = self.app.create_collection(
name=self.type, embedding_function=self.embedder_config name=self.type, embedding_function=self.embedder_config
) )
except (ImportError, AttributeError) as e:
import logging
logging.warning(f"Failed to initialize chromadb: {str(e)}. Memory functionality will be limited.")
self.app = None
self.collection = None
def _sanitize_role(self, role: str) -> str: def _sanitize_role(self, role: str) -> str:
""" """
@@ -103,6 +109,9 @@ class RAGStorage(BaseRAGStorage):
def save(self, value: Any, metadata: Dict[str, Any]) -> None: def save(self, value: Any, metadata: Dict[str, Any]) -> None:
if not hasattr(self, "app") or not hasattr(self, "collection"): if not hasattr(self, "app") or not hasattr(self, "collection"):
self._initialize_app() self._initialize_app()
if self.app is None or self.collection is None:
logging.warning("Cannot save to memory as chromadb is not available.")
return
try: try:
self._generate_embedding(value, metadata) self._generate_embedding(value, metadata)
except Exception as e: except Exception as e:
@@ -115,9 +124,13 @@ class RAGStorage(BaseRAGStorage):
filter: Optional[dict] = None, filter: Optional[dict] = None,
score_threshold: float = 0.35, score_threshold: float = 0.35,
) -> List[Any]: ) -> List[Any]:
if not hasattr(self, "app"): if not hasattr(self, "app") or not hasattr(self, "collection"):
self._initialize_app() self._initialize_app()
if self.app is None or self.collection is None:
logging.warning("Cannot search memory as chromadb is not available.")
return []
try: try:
with suppress_logging(): with suppress_logging():
response = self.collection.query(query_texts=query, n_results=limit) response = self.collection.query(query_texts=query, n_results=limit)
@@ -142,6 +155,10 @@ class RAGStorage(BaseRAGStorage):
if not hasattr(self, "app") or not hasattr(self, "collection"): if not hasattr(self, "app") or not hasattr(self, "collection"):
self._initialize_app() self._initialize_app()
if self.app is None or self.collection is None:
logging.warning("Cannot generate embeddings as chromadb is not available.")
return
self.collection.add( self.collection.add(
documents=[text], documents=[text],
metadatas=[metadata or {}], metadatas=[metadata or {}],
@@ -160,15 +177,7 @@ class RAGStorage(BaseRAGStorage):
# Ignore this specific error # Ignore this specific error
pass pass
else: else:
raise Exception( logging.error(f"An error occurred while resetting the {self.type} memory: {e}")
f"An error occurred while resetting the {self.type} memory: {e}" # Don't raise exception to prevent crashes
) self.app = None
self.collection = None
def _create_default_embedding_function(self):
from chromadb.utils.embedding_functions.openai_embedding_function import (
OpenAIEmbeddingFunction,
)
return OpenAIEmbeddingFunction(
api_key=os.getenv("OPENAI_API_KEY"), model_name="text-embedding-3-small"
)

View File

@@ -1,8 +1,29 @@
import os import os
import warnings
from typing import Any, Dict, Optional, cast from typing import Any, Dict, Optional, cast
# Initialize with None to indicate module import status
Documents = None
EmbeddingFunction = None
Embeddings = None
validate_embedding_function = None
# Try to import chromadb-related modules with proper error handling
try:
from chromadb import Documents, EmbeddingFunction, Embeddings from chromadb import Documents, EmbeddingFunction, Embeddings
from chromadb.api.types import validate_embedding_function from chromadb.api.types import validate_embedding_function
CHROMADB_AVAILABLE = True
except (ImportError, AttributeError) as e:
# This captures both ImportError and AttributeError (which can happen with NumPy 2.x)
warnings.warn(f"Failed to import chromadb: {str(e)}. Embedding functionality will be limited.")
# Define a simple embedding function interface for typehinting
class EmbeddingFunction:
def __call__(self, texts):
raise NotImplementedError("Chromadb is not available")
CHROMADB_AVAILABLE = False
def validate_embedding_function(func):
return func
class EmbeddingConfigurator: class EmbeddingConfigurator:
@@ -26,6 +47,9 @@ class EmbeddingConfigurator:
embedder_config: Optional[Dict[str, Any]] = None, embedder_config: Optional[Dict[str, Any]] = None,
) -> EmbeddingFunction: ) -> EmbeddingFunction:
"""Configures and returns an embedding function based on the provided config.""" """Configures and returns an embedding function based on the provided config."""
if not CHROMADB_AVAILABLE:
return self._create_unavailable_embedding_function()
if embedder_config is None: if embedder_config is None:
return self._create_default_embedding_function() return self._create_default_embedding_function()
@@ -45,8 +69,24 @@ class EmbeddingConfigurator:
else embedding_function(config, model_name) else embedding_function(config, model_name)
) )
@staticmethod
def _create_unavailable_embedding_function():
"""Creates a fallback embedding function when chromadb is not available."""
class UnavailableEmbeddingFunction(EmbeddingFunction):
def __call__(self, input):
raise ImportError(
"Chromadb is not available due to NumPy compatibility issues. "
"Either downgrade to NumPy<2 or upgrade chromadb and related dependencies."
)
return UnavailableEmbeddingFunction()
@staticmethod @staticmethod
def _create_default_embedding_function(): def _create_default_embedding_function():
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
try:
from chromadb.utils.embedding_functions.openai_embedding_function import ( from chromadb.utils.embedding_functions.openai_embedding_function import (
OpenAIEmbeddingFunction, OpenAIEmbeddingFunction,
) )
@@ -54,9 +94,17 @@ class EmbeddingConfigurator:
return OpenAIEmbeddingFunction( return OpenAIEmbeddingFunction(
api_key=os.getenv("OPENAI_API_KEY"), model_name="text-embedding-3-small" api_key=os.getenv("OPENAI_API_KEY"), model_name="text-embedding-3-small"
) )
except (ImportError, AttributeError) as e:
import warnings
warnings.warn(f"Failed to import OpenAIEmbeddingFunction: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
@staticmethod @staticmethod
def _configure_openai(config, model_name): def _configure_openai(config, model_name):
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
try:
from chromadb.utils.embedding_functions.openai_embedding_function import ( from chromadb.utils.embedding_functions.openai_embedding_function import (
OpenAIEmbeddingFunction, OpenAIEmbeddingFunction,
) )
@@ -72,9 +120,16 @@ class EmbeddingConfigurator:
deployment_id=config.get("deployment_id", None), deployment_id=config.get("deployment_id", None),
organization_id=config.get("organization_id", None), organization_id=config.get("organization_id", None),
) )
except (ImportError, AttributeError) as e:
warnings.warn(f"Failed to import OpenAIEmbeddingFunction: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
@staticmethod @staticmethod
def _configure_azure(config, model_name): def _configure_azure(config, model_name):
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
try:
from chromadb.utils.embedding_functions.openai_embedding_function import ( from chromadb.utils.embedding_functions.openai_embedding_function import (
OpenAIEmbeddingFunction, OpenAIEmbeddingFunction,
) )
@@ -90,9 +145,16 @@ class EmbeddingConfigurator:
deployment_id=config.get("deployment_id"), deployment_id=config.get("deployment_id"),
organization_id=config.get("organization_id"), organization_id=config.get("organization_id"),
) )
except (ImportError, AttributeError) as e:
warnings.warn(f"Failed to import OpenAIEmbeddingFunction: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
@staticmethod @staticmethod
def _configure_ollama(config, model_name): def _configure_ollama(config, model_name):
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
try:
from chromadb.utils.embedding_functions.ollama_embedding_function import ( from chromadb.utils.embedding_functions.ollama_embedding_function import (
OllamaEmbeddingFunction, OllamaEmbeddingFunction,
) )
@@ -101,9 +163,16 @@ class EmbeddingConfigurator:
url=config.get("url", "http://localhost:11434/api/embeddings"), url=config.get("url", "http://localhost:11434/api/embeddings"),
model_name=model_name, model_name=model_name,
) )
except (ImportError, AttributeError) as e:
warnings.warn(f"Failed to import OllamaEmbeddingFunction: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
@staticmethod @staticmethod
def _configure_vertexai(config, model_name): def _configure_vertexai(config, model_name):
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
try:
from chromadb.utils.embedding_functions.google_embedding_function import ( from chromadb.utils.embedding_functions.google_embedding_function import (
GoogleVertexEmbeddingFunction, GoogleVertexEmbeddingFunction,
) )
@@ -114,9 +183,16 @@ class EmbeddingConfigurator:
project_id=config.get("project_id"), project_id=config.get("project_id"),
region=config.get("region"), region=config.get("region"),
) )
except (ImportError, AttributeError) as e:
warnings.warn(f"Failed to import GoogleVertexEmbeddingFunction: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
@staticmethod @staticmethod
def _configure_google(config, model_name): def _configure_google(config, model_name):
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
try:
from chromadb.utils.embedding_functions.google_embedding_function import ( from chromadb.utils.embedding_functions.google_embedding_function import (
GoogleGenerativeAiEmbeddingFunction, GoogleGenerativeAiEmbeddingFunction,
) )
@@ -126,9 +202,16 @@ class EmbeddingConfigurator:
api_key=config.get("api_key"), api_key=config.get("api_key"),
task_type=config.get("task_type"), task_type=config.get("task_type"),
) )
except (ImportError, AttributeError) as e:
warnings.warn(f"Failed to import GoogleGenerativeAiEmbeddingFunction: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
@staticmethod @staticmethod
def _configure_cohere(config, model_name): def _configure_cohere(config, model_name):
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
try:
from chromadb.utils.embedding_functions.cohere_embedding_function import ( from chromadb.utils.embedding_functions.cohere_embedding_function import (
CohereEmbeddingFunction, CohereEmbeddingFunction,
) )
@@ -137,9 +220,16 @@ class EmbeddingConfigurator:
model_name=model_name, model_name=model_name,
api_key=config.get("api_key"), api_key=config.get("api_key"),
) )
except (ImportError, AttributeError) as e:
warnings.warn(f"Failed to import CohereEmbeddingFunction: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
@staticmethod @staticmethod
def _configure_voyageai(config, model_name): def _configure_voyageai(config, model_name):
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
try:
from chromadb.utils.embedding_functions.voyageai_embedding_function import ( from chromadb.utils.embedding_functions.voyageai_embedding_function import (
VoyageAIEmbeddingFunction, VoyageAIEmbeddingFunction,
) )
@@ -148,9 +238,16 @@ class EmbeddingConfigurator:
model_name=model_name, model_name=model_name,
api_key=config.get("api_key"), api_key=config.get("api_key"),
) )
except (ImportError, AttributeError) as e:
warnings.warn(f"Failed to import VoyageAIEmbeddingFunction: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
@staticmethod @staticmethod
def _configure_bedrock(config, model_name): def _configure_bedrock(config, model_name):
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
try:
from chromadb.utils.embedding_functions.amazon_bedrock_embedding_function import ( from chromadb.utils.embedding_functions.amazon_bedrock_embedding_function import (
AmazonBedrockEmbeddingFunction, AmazonBedrockEmbeddingFunction,
) )
@@ -160,9 +257,16 @@ class EmbeddingConfigurator:
if model_name is not None: if model_name is not None:
kwargs["model_name"] = model_name kwargs["model_name"] = model_name
return AmazonBedrockEmbeddingFunction(**kwargs) return AmazonBedrockEmbeddingFunction(**kwargs)
except (ImportError, AttributeError) as e:
warnings.warn(f"Failed to import AmazonBedrockEmbeddingFunction: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
@staticmethod @staticmethod
def _configure_huggingface(config, model_name): def _configure_huggingface(config, model_name):
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
try:
from chromadb.utils.embedding_functions.huggingface_embedding_function import ( from chromadb.utils.embedding_functions.huggingface_embedding_function import (
HuggingFaceEmbeddingServer, HuggingFaceEmbeddingServer,
) )
@@ -170,17 +274,24 @@ class EmbeddingConfigurator:
return HuggingFaceEmbeddingServer( return HuggingFaceEmbeddingServer(
url=config.get("api_url"), url=config.get("api_url"),
) )
except (ImportError, AttributeError) as e:
warnings.warn(f"Failed to import HuggingFaceEmbeddingServer: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
@staticmethod @staticmethod
def _configure_watson(config, model_name): def _configure_watson(config, model_name):
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
try: try:
import ibm_watsonx_ai.foundation_models as watson_models import ibm_watsonx_ai.foundation_models as watson_models
from ibm_watsonx_ai import Credentials from ibm_watsonx_ai import Credentials
from ibm_watsonx_ai.metanames import EmbedTextParamsMetaNames as EmbedParams from ibm_watsonx_ai.metanames import EmbedTextParamsMetaNames as EmbedParams
except ImportError as e: except ImportError as e:
raise ImportError( warnings.warn(
"IBM Watson dependencies are not installed. Please install them to use Watson embedding." "IBM Watson dependencies are not installed. Please install them to use Watson embedding."
) from e )
return EmbeddingConfigurator._create_unavailable_embedding_function()
class WatsonEmbeddingFunction(EmbeddingFunction): class WatsonEmbeddingFunction(EmbeddingFunction):
def __call__(self, input: Documents) -> Embeddings: def __call__(self, input: Documents) -> Embeddings:
@@ -212,25 +323,30 @@ class EmbeddingConfigurator:
@staticmethod @staticmethod
def _configure_custom(config): def _configure_custom(config):
if not CHROMADB_AVAILABLE:
return EmbeddingConfigurator._create_unavailable_embedding_function()
custom_embedder = config.get("embedder") custom_embedder = config.get("embedder")
if isinstance(custom_embedder, EmbeddingFunction): if isinstance(custom_embedder, EmbeddingFunction):
try: try:
validate_embedding_function(custom_embedder) validate_embedding_function(custom_embedder)
return custom_embedder return custom_embedder
except Exception as e: except Exception as e:
raise ValueError(f"Invalid custom embedding function: {str(e)}") warnings.warn(f"Invalid custom embedding function: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
elif callable(custom_embedder): elif callable(custom_embedder):
try: try:
instance = custom_embedder() instance = custom_embedder()
if isinstance(instance, EmbeddingFunction): if isinstance(instance, EmbeddingFunction):
validate_embedding_function(instance) validate_embedding_function(instance)
return instance return instance
raise ValueError( warnings.warn("Custom embedder does not create an EmbeddingFunction instance")
"Custom embedder does not create an EmbeddingFunction instance" return EmbeddingConfigurator._create_unavailable_embedding_function()
)
except Exception as e: except Exception as e:
raise ValueError(f"Error instantiating custom embedder: {str(e)}") warnings.warn(f"Error instantiating custom embedder: {str(e)}")
return EmbeddingConfigurator._create_unavailable_embedding_function()
else: else:
raise ValueError( warnings.warn(
"Custom embedder must be an instance of `EmbeddingFunction` or a callable that creates one" "Custom embedder must be an instance of `EmbeddingFunction` or a callable that creates one"
) )
return EmbeddingConfigurator._create_unavailable_embedding_function()

View File

@@ -0,0 +1,62 @@
import pytest
import sys
import importlib
import warnings
def test_crew_import_with_numpy():
"""Test that crewai can be imported even with NumPy compatibility issues."""
try:
# Force reload to ensure we test our fix
if "crewai" in sys.modules:
importlib.reload(sys.modules["crewai"])
# This should not raise an exception
from crewai import Crew
assert Crew is not None
except Exception as e:
pytest.fail(f"Failed to import Crew: {e}")
def test_embedding_configurator_with_numpy():
"""Test that EmbeddingConfigurator can be imported with NumPy."""
try:
# Force reload
if "crewai.utilities.embedding_configurator" in sys.modules:
importlib.reload(sys.modules["crewai.utilities.embedding_configurator"])
from crewai.utilities.embedding_configurator import EmbeddingConfigurator
configurator = EmbeddingConfigurator()
# Test that we can create an embedder (might be unavailable but shouldn't crash)
embedder = configurator.configure_embedder()
assert embedder is not None
except Exception as e:
pytest.fail(f"Failed to use EmbeddingConfigurator: {e}")
def test_rag_storage_with_numpy():
"""Test that RAGStorage can be imported and used with NumPy."""
try:
# Force reload
if "crewai.memory.storage.rag_storage" in sys.modules:
importlib.reload(sys.modules["crewai.memory.storage.rag_storage"])
from crewai.memory.storage.rag_storage import RAGStorage
# Initialize with minimal config to avoid actual DB operations
storage = RAGStorage(type="test", crew=None)
# Just verify we can create the object without errors
assert storage is not None
except Exception as e:
pytest.fail(f"Failed to use RAGStorage: {e}")
def test_knowledge_storage_with_numpy():
"""Test that KnowledgeStorage can be imported and used with NumPy."""
try:
# Force reload
if "crewai.knowledge.storage.knowledge_storage" in sys.modules:
importlib.reload(sys.modules["crewai.knowledge.storage.knowledge_storage"])
from crewai.knowledge.storage.knowledge_storage import KnowledgeStorage
# Initialize with minimal config
storage = KnowledgeStorage()
# Just verify we can create the object without errors
assert storage is not None
except Exception as e:
pytest.fail(f"Failed to use KnowledgeStorage: {e}")