mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-08 15:48:29 +00:00
* WIP crew events emitter * Refactor event handling and introduce new event types - Migrate from global `emit` function to `event_bus.emit` - Add new event types for task failures, tool usage, and agent execution - Update event listeners and event bus to support more granular event tracking - Remove deprecated event emission methods - Improve event type consistency and add more detailed event information * Add event emission for agent execution lifecycle - Emit AgentExecutionStarted and AgentExecutionError events - Update CrewAgentExecutor to use event_bus for tracking agent execution - Refactor error handling to include event emission - Minor code formatting improvements in task.py and crew_agent_executor.py - Fix a typo in test file * Refactor event system and add third-party event listeners - Move event_bus import to correct module paths - Introduce BaseEventListener abstract base class - Add AgentOpsListener for third-party event tracking - Update event listener initialization and setup - Clean up event-related imports and exports * Enhance event system type safety and error handling - Improve type annotations for event bus and event types - Add null checks for agent and task in event emissions - Update import paths for base tool and base agent - Refactor event listener type hints - Remove unnecessary print statements - Update test configurations to match new event handling * Refactor event classes to improve type safety and naming consistency - Rename event classes to have explicit 'Event' suffix (e.g., TaskStartedEvent) - Update import statements and references across multiple files - Remove deprecated events.py module - Enhance event type hints and configurations - Clean up unnecessary event-related code * Add default model for CrewEvaluator and fix event import order - Set default model to "gpt-4o-mini" in CrewEvaluator when no model is specified - Reorder event-related imports in task.py to follow standard import conventions - Update event bus initialization method return type hint - Export event_bus in events/__init__.py * Fix tool usage and event import handling - Update tool usage to use `.get()` method when checking tool name - Remove unnecessary `__all__` export list in events/__init__.py * Refactor Flow and Agent event handling to use event_bus - Remove `event_emitter` from Flow class and replace with `event_bus.emit()` - Update Flow and Agent tests to use event_bus event listeners - Remove redundant event emissions in Flow methods - Add debug print statements in Flow execution - Simplify event tracking in test cases * Enhance event handling for Crew, Task, and Event classes - Add crew name to failed event types (CrewKickoffFailedEvent, CrewTrainFailedEvent, CrewTestFailedEvent) - Update Task events to remove redundant task and context attributes - Refactor EventListener to use Logger for consistent event logging - Add new event types for Crew train and test events - Improve event bus event tracking in test cases * Remove telemetry and tracing dependencies from Task and Flow classes - Remove telemetry-related imports and private attributes from Task class - Remove `_telemetry` attribute from Flow class - Update event handling to emit events without direct telemetry tracking - Simplify task and flow execution by removing explicit telemetry spans - Move telemetry-related event handling to EventListener * Clean up unused imports and event-related code - Remove unused imports from various event and flow-related files - Reorder event imports to follow standard conventions - Remove unnecessary event type references - Simplify import statements in event and flow modules * Update crew test to validate verbose output and kickoff_for_each method - Enhance test_crew_verbose_output to check specific listener log messages - Modify test_kickoff_for_each_invalid_input to use Pydantic validation error - Improve test coverage for crew logging and input validation * Update crew test verbose output with improved emoji icons - Replace task and agent completion icons from 👍 to ✅ - Enhance readability of test output logging - Maintain consistent test coverage for crew verbose output * Add MethodExecutionFailedEvent to handle flow method execution failures - Introduce new MethodExecutionFailedEvent in flow_events module - Update Flow class to catch and emit method execution failures - Add event listener for method execution failure events - Update event-related imports to include new event type - Enhance test coverage for method execution failure handling * Propagate method execution failures in Flow class - Modify Flow class to re-raise exceptions after emitting MethodExecutionFailedEvent - Reorder MethodExecutionFailedEvent import to maintain consistent import style * Enable test coverage for Flow method execution failure event - Uncomment pytest.raises() in test_events to verify exception handling - Ensure test validates MethodExecutionFailedEvent emission during flow kickoff * Add event handling for tool usage events - Introduce event listeners for ToolUsageFinishedEvent and ToolUsageErrorEvent - Log tool usage events with descriptive emoji icons (✅ and ❌) - Update event_listener to track and log tool usage lifecycle * Reorder and clean up event imports in event_listener - Reorganize imports for tool usage events and other event types - Maintain consistent import ordering and remove unused imports - Ensure clean and organized import structure in event_listener module * moving to dedicated eventlistener * dont forget crew level * Refactor AgentOps event listener for crew-level tracking - Modify AgentOpsListener to handle crew-level events - Initialize and end AgentOps session at crew kickoff and completion - Create agents for each crew member during session initialization - Improve session management and event recording - Clean up and simplify event handling logic * Update test_events to validate tool usage error event handling - Modify test to assert single error event with correct attributes - Use pytest.raises() to verify error event generation - Simplify error event validation in test case * Improve AgentOps listener type hints and formatting - Add string type hints for AgentOps classes to resolve potential import issues - Clean up unnecessary whitespace and improve code indentation - Simplify initialization and event handling logic * Update test_events to validate multiple tool usage events - Modify test to assert 75 events instead of a single error event - Remove pytest.raises() check, allowing crew kickoff to complete - Adjust event validation to support broader event tracking * Rename event_bus to crewai_event_bus for improved clarity and specificity - Replace all references to `event_bus` with `crewai_event_bus` - Update import statements across multiple files - Remove the old `event_bus.py` file - Maintain existing event handling functionality * Enhance EventListener with singleton pattern and color configuration - Implement singleton pattern for EventListener to ensure single instance - Add default color configuration using EMITTER_COLOR from constants - Modify log method calls to use default color and remove redundant color parameters - Improve initialization logic to prevent multiple initializations * Add FlowPlotEvent and update event bus to support flow plotting - Introduce FlowPlotEvent to track flow plotting events - Replace Telemetry method with event bus emission in Flow.plot() - Update event bus to support new FlowPlotEvent type - Add test case to validate flow plotting event emission * Remove RunType enum and clean up crew events module - Delete unused RunType enum from crew_events.py - Simplify crew_events.py by removing unnecessary enum definition - Improve code clarity by removing unneeded imports * Enhance event handling for tool usage and agent execution - Add new events for tool usage: ToolSelectionErrorEvent, ToolValidateInputErrorEvent - Improve error tracking and event emission in ToolUsage and LLM classes - Update AgentExecutionStartedEvent to use task_prompt instead of inputs - Add comprehensive test coverage for new event types and error scenarios * Refactor event system and improve crew testing - Extract base CrewEvent class to a new base_events.py module - Update event imports across multiple event-related files - Modify CrewTestStartedEvent to use eval_llm instead of openai_model_name - Add LLM creation validation in crew testing method - Improve type handling and event consistency * Refactor task events to use base CrewEvent - Move CrewEvent import from crew_events to base_events - Remove unnecessary blank lines in task_events.py - Simplify event class structure for task-related events * Update AgentExecutionStartedEvent to use task_prompt - Modify test_events.py to use task_prompt instead of inputs - Simplify event input validation in test case - Align with recent event system refactoring * Improve type hinting for TaskCompletedEvent handler - Add explicit type annotation for TaskCompletedEvent in event_listener.py - Enhance type safety for event handling in EventListener * Improve test_validate_tool_input_invalid_input with mock objects - Add explicit mock objects for agent and action in test case - Ensure proper string values for mock agent and action attributes - Simplify test setup for ToolUsage validation method * Remove ToolUsageStartedEvent emission in tool usage process - Remove unnecessary event emission for tool usage start - Simplify tool usage event handling - Eliminate redundant event data preparation step * refactor: clean up and organize imports in llm and flow modules * test: Improve flow persistence test cases and logging
202 lines
7.0 KiB
Python
202 lines
7.0 KiB
Python
import contextlib
|
|
import hashlib
|
|
import io
|
|
import logging
|
|
import os
|
|
import shutil
|
|
from typing import Any, Dict, List, Optional, Union, cast
|
|
|
|
import chromadb
|
|
import chromadb.errors
|
|
from chromadb.api import ClientAPI
|
|
from chromadb.api.types import OneOrMany
|
|
from chromadb.config import Settings
|
|
|
|
from crewai.knowledge.storage.base_knowledge_storage import BaseKnowledgeStorage
|
|
from crewai.utilities import EmbeddingConfigurator
|
|
from crewai.utilities.constants import KNOWLEDGE_DIRECTORY
|
|
from crewai.utilities.logger import Logger
|
|
from crewai.utilities.paths import db_storage_path
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def suppress_logging(
|
|
logger_name="chromadb.segment.impl.vector.local_persistent_hnsw",
|
|
level=logging.ERROR,
|
|
):
|
|
logger = logging.getLogger(logger_name)
|
|
original_level = logger.getEffectiveLevel()
|
|
logger.setLevel(level)
|
|
with (
|
|
contextlib.redirect_stdout(io.StringIO()),
|
|
contextlib.redirect_stderr(io.StringIO()),
|
|
contextlib.suppress(UserWarning),
|
|
):
|
|
yield
|
|
logger.setLevel(original_level)
|
|
|
|
|
|
class KnowledgeStorage(BaseKnowledgeStorage):
|
|
"""
|
|
Extends Storage to handle embeddings for memory entries, improving
|
|
search efficiency.
|
|
"""
|
|
|
|
collection: Optional[chromadb.Collection] = None
|
|
collection_name: Optional[str] = "knowledge"
|
|
app: Optional[ClientAPI] = None
|
|
|
|
def __init__(
|
|
self,
|
|
embedder: Optional[Dict[str, Any]] = None,
|
|
collection_name: Optional[str] = None,
|
|
):
|
|
self.collection_name = collection_name
|
|
self._set_embedder_config(embedder)
|
|
|
|
def search(
|
|
self,
|
|
query: List[str],
|
|
limit: int = 3,
|
|
filter: Optional[dict] = None,
|
|
score_threshold: float = 0.35,
|
|
) -> List[Dict[str, Any]]:
|
|
with suppress_logging():
|
|
if self.collection:
|
|
fetched = self.collection.query(
|
|
query_texts=query,
|
|
n_results=limit,
|
|
where=filter,
|
|
)
|
|
results = []
|
|
for i in range(len(fetched["ids"][0])): # type: ignore
|
|
result = {
|
|
"id": fetched["ids"][0][i], # type: ignore
|
|
"metadata": fetched["metadatas"][0][i], # type: ignore
|
|
"context": fetched["documents"][0][i], # type: ignore
|
|
"score": fetched["distances"][0][i], # type: ignore
|
|
}
|
|
if result["score"] >= score_threshold:
|
|
results.append(result)
|
|
return results
|
|
else:
|
|
raise Exception("Collection not initialized")
|
|
|
|
def initialize_knowledge_storage(self):
|
|
base_path = os.path.join(db_storage_path(), "knowledge")
|
|
chroma_client = chromadb.PersistentClient(
|
|
path=base_path,
|
|
settings=Settings(allow_reset=True),
|
|
)
|
|
|
|
self.app = chroma_client
|
|
|
|
try:
|
|
collection_name = (
|
|
f"knowledge_{self.collection_name}"
|
|
if self.collection_name
|
|
else "knowledge"
|
|
)
|
|
if self.app:
|
|
self.collection = self.app.get_or_create_collection(
|
|
name=collection_name, embedding_function=self.embedder
|
|
)
|
|
else:
|
|
raise Exception("Vector Database Client not initialized")
|
|
except Exception:
|
|
raise Exception("Failed to create or get collection")
|
|
|
|
def reset(self):
|
|
base_path = os.path.join(db_storage_path(), KNOWLEDGE_DIRECTORY)
|
|
if not self.app:
|
|
self.app = chromadb.PersistentClient(
|
|
path=base_path,
|
|
settings=Settings(allow_reset=True),
|
|
)
|
|
|
|
self.app.reset()
|
|
shutil.rmtree(base_path)
|
|
self.app = None
|
|
self.collection = None
|
|
|
|
def save(
|
|
self,
|
|
documents: List[str],
|
|
metadata: Optional[Union[Dict[str, Any], List[Dict[str, Any]]]] = None,
|
|
):
|
|
if not self.collection:
|
|
raise Exception("Collection not initialized")
|
|
|
|
try:
|
|
# Create a dictionary to store unique documents
|
|
unique_docs = {}
|
|
|
|
# Generate IDs and create a mapping of id -> (document, metadata)
|
|
for idx, doc in enumerate(documents):
|
|
doc_id = hashlib.sha256(doc.encode("utf-8")).hexdigest()
|
|
doc_metadata = None
|
|
if metadata is not None:
|
|
if isinstance(metadata, list):
|
|
doc_metadata = metadata[idx]
|
|
else:
|
|
doc_metadata = metadata
|
|
unique_docs[doc_id] = (doc, doc_metadata)
|
|
|
|
# Prepare filtered lists for ChromaDB
|
|
filtered_docs = []
|
|
filtered_metadata = []
|
|
filtered_ids = []
|
|
|
|
# Build the filtered lists
|
|
for doc_id, (doc, meta) in unique_docs.items():
|
|
filtered_docs.append(doc)
|
|
filtered_metadata.append(meta)
|
|
filtered_ids.append(doc_id)
|
|
|
|
# If we have no metadata at all, set it to None
|
|
final_metadata: Optional[OneOrMany[chromadb.Metadata]] = (
|
|
None if all(m is None for m in filtered_metadata) else filtered_metadata
|
|
)
|
|
|
|
self.collection.upsert(
|
|
documents=filtered_docs,
|
|
metadatas=final_metadata,
|
|
ids=filtered_ids,
|
|
)
|
|
except chromadb.errors.InvalidDimensionException as e:
|
|
Logger(verbose=True).log(
|
|
"error",
|
|
"Embedding dimension mismatch. This usually happens when mixing different embedding models. Try resetting the collection using `crewai reset-memories -a`",
|
|
"red",
|
|
)
|
|
raise ValueError(
|
|
"Embedding dimension mismatch. Make sure you're using the same embedding model "
|
|
"across all operations with this collection."
|
|
"Try resetting the collection using `crewai reset-memories -a`"
|
|
) from e
|
|
except Exception as e:
|
|
Logger(verbose=True).log("error", f"Failed to upsert documents: {e}", "red")
|
|
raise
|
|
|
|
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"
|
|
)
|
|
|
|
def _set_embedder_config(self, embedder: Optional[Dict[str, Any]] = None) -> None:
|
|
"""Set the embedding configuration for the knowledge storage.
|
|
|
|
Args:
|
|
embedder_config (Optional[Dict[str, Any]]): Configuration dictionary for the embedder.
|
|
If None or empty, defaults to the default embedding function.
|
|
"""
|
|
self.embedder = (
|
|
EmbeddingConfigurator().configure_embedder(embedder)
|
|
if embedder
|
|
else self._create_default_embedding_function()
|
|
)
|