mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-15 11:08:33 +00:00
feat: implement crewAI lite version with optional dependencies
- Restructure pyproject.toml to move non-essential dependencies to optional extras - Add graceful handling for missing optional dependencies in core modules - Create memory, knowledge, telemetry, visualization, auth, and llm-integrations extras - Implement helpful ImportError messages directing users to install specific extras - Add comprehensive test suite for lite installation scenarios - Maintain backward compatibility with existing installations - Support minimal core installation with Agent/Crew/Task functionality Addresses GitHub issue #3026 for lightweight crewAI installation Co-Authored-By: João <joao@crewai.com>
This commit is contained in:
@@ -5,16 +5,27 @@ from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from auth0.authentication.token_verifier import (
|
||||
AsymmetricSignatureVerifier,
|
||||
TokenVerifier,
|
||||
)
|
||||
try:
|
||||
from auth0.authentication.token_verifier import (
|
||||
AsymmetricSignatureVerifier,
|
||||
TokenVerifier,
|
||||
)
|
||||
AUTH0_AVAILABLE = True
|
||||
except ImportError:
|
||||
AUTH0_AVAILABLE = False
|
||||
AsymmetricSignatureVerifier = None
|
||||
TokenVerifier = None
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
from .constants import AUTH0_CLIENT_ID, AUTH0_DOMAIN
|
||||
|
||||
|
||||
def validate_token(id_token: str) -> None:
|
||||
if not AUTH0_AVAILABLE:
|
||||
raise ImportError(
|
||||
"Auth0 is required for authentication functionality. "
|
||||
"Please install it with: pip install 'crewai[auth]'"
|
||||
)
|
||||
"""
|
||||
Verify the token and its precedence
|
||||
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from pyvis.network import Network
|
||||
try:
|
||||
from pyvis.network import Network
|
||||
PYVIS_AVAILABLE = True
|
||||
except ImportError:
|
||||
PYVIS_AVAILABLE = False
|
||||
Network = None
|
||||
|
||||
from crewai.flow.config import COLORS, NODE_STYLES
|
||||
from crewai.flow.html_template_handler import HTMLTemplateHandler
|
||||
@@ -63,6 +68,12 @@ class FlowPlot:
|
||||
RuntimeError
|
||||
If network visualization generation fails.
|
||||
"""
|
||||
if not PYVIS_AVAILABLE:
|
||||
raise ImportError(
|
||||
"Pyvis is required for flow visualization. "
|
||||
"Please install it with: pip install 'crewai[visualization]'"
|
||||
)
|
||||
|
||||
if not filename or not isinstance(filename, str):
|
||||
raise ValueError("Filename must be a non-empty string")
|
||||
|
||||
@@ -222,5 +233,11 @@ def plot_flow(flow, filename="flow_plot"):
|
||||
IOError
|
||||
If file operations fail.
|
||||
"""
|
||||
if not PYVIS_AVAILABLE:
|
||||
raise ImportError(
|
||||
"Pyvis is required for flow visualization. "
|
||||
"Please install it with: pip install 'crewai[visualization]'"
|
||||
)
|
||||
|
||||
visualizer = FlowPlot(flow)
|
||||
visualizer.plot(filename)
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
try:
|
||||
import pdfplumber
|
||||
PDFPLUMBER_AVAILABLE = True
|
||||
except ImportError:
|
||||
PDFPLUMBER_AVAILABLE = False
|
||||
pdfplumber = None
|
||||
|
||||
from crewai.knowledge.source.base_file_knowledge_source import BaseFileKnowledgeSource
|
||||
|
||||
|
||||
@@ -26,14 +33,12 @@ class PDFKnowledgeSource(BaseFileKnowledgeSource):
|
||||
|
||||
def _import_pdfplumber(self):
|
||||
"""Dynamically import pdfplumber."""
|
||||
try:
|
||||
import pdfplumber
|
||||
|
||||
return pdfplumber
|
||||
except ImportError:
|
||||
if not PDFPLUMBER_AVAILABLE:
|
||||
raise ImportError(
|
||||
"pdfplumber is not installed. Please install it with: pip install pdfplumber"
|
||||
"pdfplumber is required for PDF knowledge sources. "
|
||||
"Please install it with: pip install 'crewai[knowledge]'"
|
||||
)
|
||||
return pdfplumber
|
||||
|
||||
def add(self) -> None:
|
||||
"""
|
||||
|
||||
@@ -6,11 +6,19 @@ import os
|
||||
import shutil
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import chromadb
|
||||
import chromadb.errors
|
||||
from chromadb.api import ClientAPI
|
||||
from chromadb.api.types import OneOrMany
|
||||
from chromadb.config import Settings
|
||||
try:
|
||||
import chromadb
|
||||
import chromadb.errors
|
||||
from chromadb.api import ClientAPI
|
||||
from chromadb.api.types import OneOrMany
|
||||
from chromadb.config import Settings
|
||||
CHROMADB_AVAILABLE = True
|
||||
except ImportError:
|
||||
CHROMADB_AVAILABLE = False
|
||||
chromadb = None
|
||||
ClientAPI = None
|
||||
OneOrMany = None
|
||||
Settings = None
|
||||
|
||||
from crewai.knowledge.storage.base_knowledge_storage import BaseKnowledgeStorage
|
||||
from crewai.utilities import EmbeddingConfigurator
|
||||
@@ -43,7 +51,7 @@ class KnowledgeStorage(BaseKnowledgeStorage):
|
||||
search efficiency.
|
||||
"""
|
||||
|
||||
collection: Optional[chromadb.Collection] = None
|
||||
collection: Optional[Any] = None
|
||||
collection_name: Optional[str] = "knowledge"
|
||||
app: Optional[ClientAPI] = None
|
||||
|
||||
@@ -52,6 +60,12 @@ class KnowledgeStorage(BaseKnowledgeStorage):
|
||||
embedder: Optional[Dict[str, Any]] = None,
|
||||
collection_name: Optional[str] = None,
|
||||
):
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for knowledge storage functionality. "
|
||||
"Please install it with: pip install 'crewai[knowledge]'"
|
||||
)
|
||||
|
||||
self.collection_name = collection_name
|
||||
self._set_embedder_config(embedder)
|
||||
|
||||
@@ -181,6 +195,12 @@ class KnowledgeStorage(BaseKnowledgeStorage):
|
||||
raise
|
||||
|
||||
def _create_default_embedding_function(self):
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for embedding functionality. "
|
||||
"Please install it with: pip install 'crewai[knowledge]'"
|
||||
)
|
||||
|
||||
from chromadb.utils.embedding_functions.openai_embedding_function import (
|
||||
OpenAIEmbeddingFunction,
|
||||
)
|
||||
|
||||
12
src/crewai/llms/third_party/ai_suite.py
vendored
12
src/crewai/llms/third_party/ai_suite.py
vendored
@@ -1,12 +1,22 @@
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import aisuite as ai
|
||||
try:
|
||||
import aisuite as ai
|
||||
AISUITE_AVAILABLE = True
|
||||
except ImportError:
|
||||
AISUITE_AVAILABLE = False
|
||||
ai = None
|
||||
|
||||
from crewai.llms.base_llm import BaseLLM
|
||||
|
||||
|
||||
class AISuiteLLM(BaseLLM):
|
||||
def __init__(self, model: str, temperature: Optional[float] = None, **kwargs):
|
||||
if not AISUITE_AVAILABLE:
|
||||
raise ImportError(
|
||||
"AISuite is required for AISuiteLLM. "
|
||||
"Please install it with: pip install 'crewai[llm-integrations]'"
|
||||
)
|
||||
super().__init__(model, temperature, **kwargs)
|
||||
self.client = ai.Client()
|
||||
|
||||
|
||||
@@ -6,7 +6,12 @@ import shutil
|
||||
import uuid
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from chromadb.api import ClientAPI
|
||||
try:
|
||||
from chromadb.api import ClientAPI
|
||||
CHROMADB_AVAILABLE = True
|
||||
except ImportError:
|
||||
CHROMADB_AVAILABLE = False
|
||||
ClientAPI = None
|
||||
|
||||
from crewai.memory.storage.base_rag_storage import BaseRAGStorage
|
||||
from crewai.utilities import EmbeddingConfigurator
|
||||
@@ -37,11 +42,17 @@ class RAGStorage(BaseRAGStorage):
|
||||
search efficiency.
|
||||
"""
|
||||
|
||||
app: ClientAPI | None = None
|
||||
app: Optional[Any] = None
|
||||
|
||||
def __init__(
|
||||
self, type, allow_reset=True, embedder_config=None, crew=None, path=None
|
||||
):
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for RAG storage functionality. "
|
||||
"Please install it with: pip install 'crewai[memory]'"
|
||||
)
|
||||
|
||||
super().__init__(type, allow_reset, embedder_config, crew)
|
||||
agents = crew.agents if crew else []
|
||||
agents = [self._sanitize_role(agent.role) for agent in agents]
|
||||
@@ -60,6 +71,12 @@ class RAGStorage(BaseRAGStorage):
|
||||
self.embedder_config = configurator.configure_embedder(self.embedder_config)
|
||||
|
||||
def _initialize_app(self):
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for RAG storage functionality. "
|
||||
"Please install it with: pip install 'crewai[memory]'"
|
||||
)
|
||||
|
||||
import chromadb
|
||||
from chromadb.config import Settings
|
||||
|
||||
@@ -165,6 +182,12 @@ class RAGStorage(BaseRAGStorage):
|
||||
)
|
||||
|
||||
def _create_default_embedding_function(self):
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for embedding functionality. "
|
||||
"Please install it with: pip install 'crewai[memory]'"
|
||||
)
|
||||
|
||||
from chromadb.utils.embedding_functions.openai_embedding_function import (
|
||||
OpenAIEmbeddingFunction,
|
||||
)
|
||||
|
||||
@@ -11,17 +11,31 @@ from importlib.metadata import version
|
||||
from typing import TYPE_CHECKING, Any, Callable, Optional
|
||||
import threading
|
||||
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
|
||||
OTLPSpanExporter,
|
||||
)
|
||||
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
||||
from opentelemetry.sdk.trace import TracerProvider
|
||||
from opentelemetry.sdk.trace.export import (
|
||||
BatchSpanProcessor,
|
||||
SpanExportResult,
|
||||
)
|
||||
from opentelemetry.trace import Span, Status, StatusCode
|
||||
try:
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
|
||||
OTLPSpanExporter,
|
||||
)
|
||||
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
||||
from opentelemetry.sdk.trace import TracerProvider
|
||||
from opentelemetry.sdk.trace.export import (
|
||||
BatchSpanProcessor,
|
||||
SpanExportResult,
|
||||
)
|
||||
from opentelemetry.trace import Span, Status, StatusCode
|
||||
OPENTELEMETRY_AVAILABLE = True
|
||||
except ImportError:
|
||||
OPENTELEMETRY_AVAILABLE = False
|
||||
trace = None
|
||||
OTLPSpanExporter = None
|
||||
SERVICE_NAME = None
|
||||
Resource = None
|
||||
TracerProvider = None
|
||||
BatchSpanProcessor = None
|
||||
SpanExportResult = None
|
||||
Span = None
|
||||
Status = None
|
||||
StatusCode = None
|
||||
|
||||
from crewai.telemetry.constants import (
|
||||
CREWAI_TELEMETRY_BASE_URL,
|
||||
@@ -43,13 +57,21 @@ if TYPE_CHECKING:
|
||||
from crewai.task import Task
|
||||
|
||||
|
||||
class SafeOTLPSpanExporter(OTLPSpanExporter):
|
||||
def export(self, spans) -> SpanExportResult:
|
||||
class SafeOTLPSpanExporter:
|
||||
def __init__(self, *args, **kwargs):
|
||||
if OPENTELEMETRY_AVAILABLE:
|
||||
self._exporter = OTLPSpanExporter(*args, **kwargs)
|
||||
else:
|
||||
self._exporter = None
|
||||
|
||||
def export(self, spans):
|
||||
if not OPENTELEMETRY_AVAILABLE or not self._exporter:
|
||||
return None
|
||||
try:
|
||||
return super().export(spans)
|
||||
return self._exporter.export(spans)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return SpanExportResult.FAILURE
|
||||
return SpanExportResult.FAILURE if SpanExportResult else None
|
||||
|
||||
|
||||
class Telemetry:
|
||||
@@ -84,7 +106,7 @@ class Telemetry:
|
||||
self.trace_set: bool = False
|
||||
self._initialized: bool = True
|
||||
|
||||
if self._is_telemetry_disabled():
|
||||
if self._is_telemetry_disabled() or not OPENTELEMETRY_AVAILABLE:
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -116,11 +138,12 @@ class Telemetry:
|
||||
return (
|
||||
os.getenv("OTEL_SDK_DISABLED", "false").lower() == "true"
|
||||
or os.getenv("CREWAI_DISABLE_TELEMETRY", "false").lower() == "true"
|
||||
or not OPENTELEMETRY_AVAILABLE
|
||||
)
|
||||
|
||||
def _should_execute_telemetry(self) -> bool:
|
||||
"""Check if telemetry operations should be executed."""
|
||||
return self.ready and not self._is_telemetry_disabled()
|
||||
return self.ready and not self._is_telemetry_disabled() and OPENTELEMETRY_AVAILABLE
|
||||
|
||||
def set_tracer(self):
|
||||
if self.ready and not self.trace_set:
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import os
|
||||
from typing import Any, Dict, Optional, cast
|
||||
|
||||
from chromadb import Documents, EmbeddingFunction, Embeddings
|
||||
from chromadb.api.types import validate_embedding_function
|
||||
try:
|
||||
from chromadb import Documents, EmbeddingFunction, Embeddings
|
||||
from chromadb.api.types import validate_embedding_function
|
||||
CHROMADB_AVAILABLE = True
|
||||
except ImportError:
|
||||
CHROMADB_AVAILABLE = False
|
||||
Documents = None
|
||||
EmbeddingFunction = None
|
||||
Embeddings = None
|
||||
|
||||
|
||||
class EmbeddingConfigurator:
|
||||
@@ -25,6 +32,11 @@ class EmbeddingConfigurator:
|
||||
self,
|
||||
embedder_config: Optional[Dict[str, Any]] = None,
|
||||
) -> EmbeddingFunction:
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for embedding configuration. "
|
||||
"Please install it with: pip install 'crewai[memory]' or 'crewai[knowledge]'"
|
||||
)
|
||||
"""Configures and returns an embedding function based on the provided config."""
|
||||
if embedder_config is None:
|
||||
return self._create_default_embedding_function()
|
||||
@@ -47,6 +59,12 @@ class EmbeddingConfigurator:
|
||||
|
||||
@staticmethod
|
||||
def _create_default_embedding_function():
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for embedding functionality. "
|
||||
"Please install it with: pip install 'crewai[memory]' or 'crewai[knowledge]'"
|
||||
)
|
||||
|
||||
from chromadb.utils.embedding_functions.openai_embedding_function import (
|
||||
OpenAIEmbeddingFunction,
|
||||
)
|
||||
@@ -57,6 +75,12 @@ class EmbeddingConfigurator:
|
||||
|
||||
@staticmethod
|
||||
def _configure_openai(config, model_name):
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for OpenAI embedding configuration. "
|
||||
"Please install it with: pip install 'crewai[memory]' or 'crewai[knowledge]'"
|
||||
)
|
||||
|
||||
from chromadb.utils.embedding_functions.openai_embedding_function import (
|
||||
OpenAIEmbeddingFunction,
|
||||
)
|
||||
@@ -75,6 +99,12 @@ class EmbeddingConfigurator:
|
||||
|
||||
@staticmethod
|
||||
def _configure_azure(config, model_name):
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for Azure embedding configuration. "
|
||||
"Please install it with: pip install 'crewai[memory]' or 'crewai[knowledge]'"
|
||||
)
|
||||
|
||||
from chromadb.utils.embedding_functions.openai_embedding_function import (
|
||||
OpenAIEmbeddingFunction,
|
||||
)
|
||||
@@ -93,6 +123,12 @@ class EmbeddingConfigurator:
|
||||
|
||||
@staticmethod
|
||||
def _configure_ollama(config, model_name):
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for Ollama embedding configuration. "
|
||||
"Please install it with: pip install 'crewai[memory]' or 'crewai[knowledge]'"
|
||||
)
|
||||
|
||||
from chromadb.utils.embedding_functions.ollama_embedding_function import (
|
||||
OllamaEmbeddingFunction,
|
||||
)
|
||||
@@ -104,6 +140,12 @@ class EmbeddingConfigurator:
|
||||
|
||||
@staticmethod
|
||||
def _configure_vertexai(config, model_name):
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for VertexAI embedding configuration. "
|
||||
"Please install it with: pip install 'crewai[memory]' or 'crewai[knowledge]'"
|
||||
)
|
||||
|
||||
from chromadb.utils.embedding_functions.google_embedding_function import (
|
||||
GoogleVertexEmbeddingFunction,
|
||||
)
|
||||
@@ -117,6 +159,12 @@ class EmbeddingConfigurator:
|
||||
|
||||
@staticmethod
|
||||
def _configure_google(config, model_name):
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for Google embedding configuration. "
|
||||
"Please install it with: pip install 'crewai[memory]' or 'crewai[knowledge]'"
|
||||
)
|
||||
|
||||
from chromadb.utils.embedding_functions.google_embedding_function import (
|
||||
GoogleGenerativeAiEmbeddingFunction,
|
||||
)
|
||||
@@ -129,6 +177,12 @@ class EmbeddingConfigurator:
|
||||
|
||||
@staticmethod
|
||||
def _configure_cohere(config, model_name):
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for Cohere embedding configuration. "
|
||||
"Please install it with: pip install 'crewai[memory]' or 'crewai[knowledge]'"
|
||||
)
|
||||
|
||||
from chromadb.utils.embedding_functions.cohere_embedding_function import (
|
||||
CohereEmbeddingFunction,
|
||||
)
|
||||
@@ -234,3 +288,11 @@ class EmbeddingConfigurator:
|
||||
raise ValueError(
|
||||
"Custom embedder must be an instance of `EmbeddingFunction` or a callable that creates one"
|
||||
)
|
||||
|
||||
def validate_embedder_config(self, config: EmbeddingFunction) -> EmbeddingFunction:
|
||||
if not CHROMADB_AVAILABLE:
|
||||
raise ImportError(
|
||||
"ChromaDB is required for embedding validation. "
|
||||
"Please install it with: pip install 'crewai[memory]' or 'crewai[knowledge]'"
|
||||
)
|
||||
return cast(EmbeddingFunction, validate_embedding_function(config))
|
||||
|
||||
Reference in New Issue
Block a user