mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-10 00:28:31 +00:00
fix: Remove OpenAI dependency for memory reset when using alternative LLMs
- Add environment variables for default embedding provider - Support Ollama as default embedding provider - Add tests for memory reset with different providers - Update documentation Fixes #2023 Co-Authored-By: Joe Moura <joao@crewai.com>
This commit is contained in:
@@ -34,3 +34,43 @@ crewai reset-memories -a
|
|||||||
```
|
```
|
||||||
|
|
||||||
The memory system will use the configured embedding provider for all operations, including memory reset.
|
The memory system will use the configured embedding provider for all operations, including memory reset.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. OpenAI API Key Missing
|
||||||
|
```bash
|
||||||
|
# Error: EmbeddingConfigurationError: Invalid configuration for OpenAI provider
|
||||||
|
# Solution: Set your OpenAI API key
|
||||||
|
export OPENAI_API_KEY=your_key
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Ollama Connection Issues
|
||||||
|
```bash
|
||||||
|
# Error: Failed to connect to Ollama server
|
||||||
|
# Solution: Ensure Ollama is running and accessible
|
||||||
|
curl http://localhost:11434/api/embeddings
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Memory Reset Permission Issues
|
||||||
|
```bash
|
||||||
|
# Error: Failed to reset memory (readonly database)
|
||||||
|
# Solution: Check file permissions or use custom path
|
||||||
|
export CREWAI_MEMORY_PATH=/path/with/write/access
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Storage Path
|
||||||
|
|
||||||
|
You can configure a custom storage path for memory files:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from crewai.memory import ShortTermMemory
|
||||||
|
|
||||||
|
memory = ShortTermMemory(path="/custom/storage/path")
|
||||||
|
```
|
||||||
|
|
||||||
|
This is useful when:
|
||||||
|
- Default storage location has permission issues
|
||||||
|
- You want to isolate memory storage for different crews
|
||||||
|
- You need to manage memory persistence manually
|
||||||
|
|||||||
@@ -85,11 +85,10 @@ class RAGStorage(BaseRAGStorage):
|
|||||||
|
|
||||||
self._set_embedder_config()
|
self._set_embedder_config()
|
||||||
try:
|
try:
|
||||||
chroma_client = chromadb.PersistentClient(
|
self.app = chromadb.PersistentClient(
|
||||||
path=self.path if self.path else self.storage_file_name,
|
path=self.path if self.path else self.storage_file_name,
|
||||||
settings=Settings(allow_reset=self.allow_reset),
|
settings=Settings(allow_reset=self.allow_reset),
|
||||||
)
|
)
|
||||||
self.app = chroma_client
|
|
||||||
if not self.app:
|
if not self.app:
|
||||||
raise RuntimeError("Failed to initialize ChromaDB client")
|
raise RuntimeError("Failed to initialize ChromaDB client")
|
||||||
|
|
||||||
@@ -104,19 +103,6 @@ class RAGStorage(BaseRAGStorage):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError(f"Failed to initialize ChromaDB: {str(e)}")
|
raise RuntimeError(f"Failed to initialize ChromaDB: {str(e)}")
|
||||||
|
|
||||||
self.app = chroma_client
|
|
||||||
if not self.app:
|
|
||||||
raise RuntimeError("Failed to initialize ChromaDB client")
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.collection = self.app.get_collection(
|
|
||||||
name=self.type, embedding_function=self.embedder_config
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
self.collection = self.app.create_collection(
|
|
||||||
name=self.type, embedding_function=self.embedder_config
|
|
||||||
)
|
|
||||||
|
|
||||||
def _sanitize_role(self, role: str) -> str:
|
def _sanitize_role(self, role: str) -> str:
|
||||||
"""
|
"""
|
||||||
Sanitizes agent roles to ensure valid directory names.
|
Sanitizes agent roles to ensure valid directory names.
|
||||||
@@ -138,12 +124,21 @@ class RAGStorage(BaseRAGStorage):
|
|||||||
return f"{base_path}/{file_name}"
|
return f"{base_path}/{file_name}"
|
||||||
|
|
||||||
def save(self, value: Any, metadata: Dict[str, Any]) -> None:
|
def save(self, value: Any, metadata: Dict[str, Any]) -> None:
|
||||||
|
"""Save a value with metadata to the memory storage.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The text content to store
|
||||||
|
metadata: Additional metadata for the stored content
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
EmbeddingInitializationError: If embedding generation fails
|
||||||
|
"""
|
||||||
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()
|
||||||
try:
|
try:
|
||||||
self._generate_embedding(value, metadata)
|
self._generate_embedding(value, metadata)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error during {self.type} save: {str(e)}")
|
raise EmbeddingInitializationError(self.type, str(e))
|
||||||
|
|
||||||
def search(
|
def search(
|
||||||
self,
|
self,
|
||||||
@@ -151,7 +146,18 @@ class RAGStorage(BaseRAGStorage):
|
|||||||
limit: int = 3,
|
limit: int = 3,
|
||||||
filter: Optional[dict] = None,
|
filter: Optional[dict] = None,
|
||||||
score_threshold: float = 0.35,
|
score_threshold: float = 0.35,
|
||||||
) -> List[Any]:
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""Search for similar content in memory.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
query: The search query text
|
||||||
|
limit: Maximum number of results to return
|
||||||
|
filter: Optional filter criteria
|
||||||
|
score_threshold: Minimum similarity score threshold
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of matching results with metadata and scores
|
||||||
|
"""
|
||||||
if not hasattr(self, "app"):
|
if not hasattr(self, "app"):
|
||||||
self._initialize_app()
|
self._initialize_app()
|
||||||
|
|
||||||
@@ -175,15 +181,24 @@ class RAGStorage(BaseRAGStorage):
|
|||||||
logging.error(f"Error during {self.type} search: {str(e)}")
|
logging.error(f"Error during {self.type} search: {str(e)}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def _generate_embedding(self, text: str, metadata: Dict[str, Any]) -> None: # type: ignore
|
def _generate_embedding(self, text: str, metadata: Dict[str, Any]) -> None:
|
||||||
|
"""Generate and store embeddings for the given text.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: The text to generate embeddings for
|
||||||
|
metadata: Additional metadata to store with the embeddings
|
||||||
|
"""
|
||||||
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()
|
||||||
|
|
||||||
self.collection.add(
|
try:
|
||||||
documents=[text],
|
self.collection.add(
|
||||||
metadatas=[metadata or {}],
|
documents=[text],
|
||||||
ids=[str(uuid.uuid4())],
|
metadatas=[metadata or {}],
|
||||||
)
|
ids=[str(uuid.uuid4())],
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise EmbeddingInitializationError(self.type, f"Failed to generate embedding: {str(e)}")
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
"""Reset the memory storage by clearing the database and removing files.
|
"""Reset the memory storage by clearing the database and removing files.
|
||||||
|
|||||||
@@ -71,8 +71,11 @@ class EmbeddingConfigurator:
|
|||||||
model = os.getenv("CREWAI_EMBEDDING_MODEL", DEFAULT_EMBEDDING_MODEL)
|
model = os.getenv("CREWAI_EMBEDDING_MODEL", DEFAULT_EMBEDDING_MODEL)
|
||||||
|
|
||||||
if provider == "openai":
|
if provider == "openai":
|
||||||
|
api_key = os.getenv("OPENAI_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
raise EmbeddingConfigurationError("OpenAI API key is required but not provided")
|
||||||
from chromadb.utils.embedding_functions.openai_embedding_function import OpenAIEmbeddingFunction
|
from chromadb.utils.embedding_functions.openai_embedding_function import OpenAIEmbeddingFunction
|
||||||
return OpenAIEmbeddingFunction(api_key=os.getenv("OPENAI_API_KEY"), model_name=model)
|
return OpenAIEmbeddingFunction(api_key=api_key, model_name=model)
|
||||||
elif provider == "ollama":
|
elif provider == "ollama":
|
||||||
from chromadb.utils.embedding_functions.ollama_embedding_function import OllamaEmbeddingFunction
|
from chromadb.utils.embedding_functions.ollama_embedding_function import OllamaEmbeddingFunction
|
||||||
url = os.getenv("CREWAI_OLLAMA_URL", "http://localhost:11434/api/embeddings")
|
url = os.getenv("CREWAI_OLLAMA_URL", "http://localhost:11434/api/embeddings")
|
||||||
|
|||||||
@@ -48,6 +48,19 @@ def test_memory_reset_with_invalid_provider(temp_db_dir):
|
|||||||
for memory in memories:
|
for memory in memories:
|
||||||
memory.reset()
|
memory.reset()
|
||||||
|
|
||||||
|
def test_memory_reset_with_invalid_configuration(temp_db_dir):
|
||||||
|
os.environ["CREWAI_EMBEDDING_PROVIDER"] = "openai"
|
||||||
|
os.environ.pop("OPENAI_API_KEY", None)
|
||||||
|
|
||||||
|
with pytest.raises(EmbeddingConfigurationError):
|
||||||
|
memories = [
|
||||||
|
ShortTermMemory(path=temp_db_dir),
|
||||||
|
LongTermMemory(path=temp_db_dir),
|
||||||
|
EntityMemory(path=temp_db_dir)
|
||||||
|
]
|
||||||
|
for memory in memories:
|
||||||
|
memory.reset()
|
||||||
|
|
||||||
def test_memory_reset_with_missing_ollama_url(temp_db_dir):
|
def test_memory_reset_with_missing_ollama_url(temp_db_dir):
|
||||||
os.environ["CREWAI_EMBEDDING_PROVIDER"] = "ollama"
|
os.environ["CREWAI_EMBEDDING_PROVIDER"] = "ollama"
|
||||||
os.environ.pop("CREWAI_OLLAMA_URL", None)
|
os.environ.pop("CREWAI_OLLAMA_URL", None)
|
||||||
@@ -59,3 +72,20 @@ def test_memory_reset_with_missing_ollama_url(temp_db_dir):
|
|||||||
]
|
]
|
||||||
for memory in memories:
|
for memory in memories:
|
||||||
memory.reset()
|
memory.reset()
|
||||||
|
|
||||||
|
def test_memory_reset_with_custom_path(temp_db_dir):
|
||||||
|
os.environ["CREWAI_EMBEDDING_PROVIDER"] = "ollama"
|
||||||
|
custom_path = os.path.join(temp_db_dir, "custom")
|
||||||
|
os.makedirs(custom_path, exist_ok=True)
|
||||||
|
|
||||||
|
memories = [
|
||||||
|
ShortTermMemory(path=custom_path),
|
||||||
|
LongTermMemory(path=custom_path),
|
||||||
|
EntityMemory(path=custom_path)
|
||||||
|
]
|
||||||
|
for memory in memories:
|
||||||
|
memory.reset()
|
||||||
|
|
||||||
|
assert not os.path.exists(os.path.join(custom_path, "short_term"))
|
||||||
|
assert not os.path.exists(os.path.join(custom_path, "long_term"))
|
||||||
|
assert not os.path.exists(os.path.join(custom_path, "entity"))
|
||||||
|
|||||||
Reference in New Issue
Block a user