From a3ad2c1957111764295a10630ab5d300e29bc1b2 Mon Sep 17 00:00:00 2001 From: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com> Date: Wed, 29 Jan 2025 06:37:22 -0800 Subject: [PATCH] fix breakage when cloning agent/crew using knowledge_sources and enable custom knowledge_storage (#1927) * fix breakage when cloning agent/crew using knowledge_sources * fixed typo * better * ensure use of other knowledge storage works * fix copy and custom storage * added tests * normalized name * updated cassette * fix test * remove fixture * fixed test * fix * add fixture to this * add fixture to this * patch twice since * fix again * with fixtures * better mocks * fix * simple * try * another * hopefully fixes test * hopefully fixes test * this should fix it ! * WIP: test check with prints * try this * exclude knowledge * fixes * just drop clone for now * rm print statements * printing agent_copy * checker * linted * cleanup * better docs --------- Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com> --- docs/concepts/agents.mdx | 4 +- docs/concepts/knowledge.mdx | 7 + src/crewai/agent.py | 19 +- src/crewai/agents/agent_builder/base_agent.py | 50 ++++- src/crewai/crew.py | 23 +- src/crewai/knowledge/knowledge.py | 25 ++- .../source/base_file_knowledge_source.py | 8 +- .../knowledge/storage/knowledge_storage.py | 16 +- .../utilities/embedding_configurator.py | 1 - tests/agent_test.py | 42 +++- ...ith_knowledge_sources_works_with_copy.yaml | 206 ++++++++++++++++++ ...ith_knowledge_sources_works_with_copy.yaml | 206 ++++++++++++++++++ tests/crew_test.py | 111 ++++++---- 13 files changed, 626 insertions(+), 92 deletions(-) create mode 100644 tests/cassettes/test_agent_with_knowledge_sources_works_with_copy.yaml create mode 100644 tests/cassettes/test_crew_with_knowledge_sources_works_with_copy.yaml diff --git a/docs/concepts/agents.mdx b/docs/concepts/agents.mdx index 2f81138a7..b81099386 100644 --- a/docs/concepts/agents.mdx +++ b/docs/concepts/agents.mdx @@ -43,7 +43,7 @@ Think of an agent as a specialized team member with specific skills, expertise, | **Max Retry Limit** _(optional)_ | `max_retry_limit` | `int` | Maximum number of retries when an error occurs. Default is 2. | | **Respect Context Window** _(optional)_ | `respect_context_window` | `bool` | Keep messages under context window size by summarizing. Default is True. | | **Code Execution Mode** _(optional)_ | `code_execution_mode` | `Literal["safe", "unsafe"]` | Mode for code execution: 'safe' (using Docker) or 'unsafe' (direct). Default is 'safe'. | -| **Embedder Config** _(optional)_ | `embedder_config` | `Optional[Dict[str, Any]]` | Configuration for the embedder used by the agent. | +| **Embedder** _(optional)_ | `embedder` | `Optional[Dict[str, Any]]` | Configuration for the embedder used by the agent. | | **Knowledge Sources** _(optional)_ | `knowledge_sources` | `Optional[List[BaseKnowledgeSource]]` | Knowledge sources available to the agent. | | **Use System Prompt** _(optional)_ | `use_system_prompt` | `Optional[bool]` | Whether to use system prompt (for o1 model support). Default is True. | @@ -152,7 +152,7 @@ agent = Agent( use_system_prompt=True, # Default: True tools=[SerperDevTool()], # Optional: List of tools knowledge_sources=None, # Optional: List of knowledge sources - embedder_config=None, # Optional: Custom embedder configuration + embedder=None, # Optional: Custom embedder configuration system_template=None, # Optional: Custom system prompt template prompt_template=None, # Optional: Custom prompt template response_template=None, # Optional: Custom response template diff --git a/docs/concepts/knowledge.mdx b/docs/concepts/knowledge.mdx index e4e40ba3e..78443ecab 100644 --- a/docs/concepts/knowledge.mdx +++ b/docs/concepts/knowledge.mdx @@ -324,6 +324,13 @@ agent = Agent( verbose=True, allow_delegation=False, llm=gemini_llm, + embedder={ + "provider": "google", + "config": { + "model": "models/text-embedding-004", + "api_key": GEMINI_API_KEY, + } + } ) task = Task( diff --git a/src/crewai/agent.py b/src/crewai/agent.py index 3a4d083d4..dec0effd7 100644 --- a/src/crewai/agent.py +++ b/src/crewai/agent.py @@ -61,6 +61,7 @@ class Agent(BaseAgent): tools: Tools at agents disposal step_callback: Callback to be executed after each step of the agent execution. knowledge_sources: Knowledge sources for the agent. + embedder: Embedder configuration for the agent. """ _times_executed: int = PrivateAttr(default=0) @@ -122,17 +123,10 @@ class Agent(BaseAgent): default="safe", description="Mode for code execution: 'safe' (using Docker) or 'unsafe' (direct execution).", ) - embedder_config: Optional[Dict[str, Any]] = Field( + embedder: Optional[Dict[str, Any]] = Field( default=None, description="Embedder configuration for the agent.", ) - knowledge_sources: Optional[List[BaseKnowledgeSource]] = Field( - default=None, - description="Knowledge sources for the agent.", - ) - _knowledge: Optional[Knowledge] = PrivateAttr( - default=None, - ) @model_validator(mode="after") def post_init_setup(self): @@ -163,10 +157,11 @@ class Agent(BaseAgent): if isinstance(self.knowledge_sources, list) and all( isinstance(k, BaseKnowledgeSource) for k in self.knowledge_sources ): - self._knowledge = Knowledge( + self.knowledge = Knowledge( sources=self.knowledge_sources, - embedder_config=self.embedder_config, + embedder=self.embedder, collection_name=knowledge_agent_name, + storage=self.knowledge_storage or None, ) except (TypeError, ValueError) as e: raise ValueError(f"Invalid Knowledge Configuration: {str(e)}") @@ -225,8 +220,8 @@ class Agent(BaseAgent): if memory.strip() != "": task_prompt += self.i18n.slice("memory").format(memory=memory) - if self._knowledge: - agent_knowledge_snippets = self._knowledge.query([task.prompt()]) + if self.knowledge: + agent_knowledge_snippets = self.knowledge.query([task.prompt()]) if agent_knowledge_snippets: agent_knowledge_context = extract_knowledge_context( agent_knowledge_snippets diff --git a/src/crewai/agents/agent_builder/base_agent.py b/src/crewai/agents/agent_builder/base_agent.py index 207a1769a..a8c829a2a 100644 --- a/src/crewai/agents/agent_builder/base_agent.py +++ b/src/crewai/agents/agent_builder/base_agent.py @@ -18,6 +18,8 @@ from pydantic_core import PydanticCustomError from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess from crewai.agents.cache.cache_handler import CacheHandler from crewai.agents.tools_handler import ToolsHandler +from crewai.knowledge.knowledge import Knowledge +from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource from crewai.tools import BaseTool from crewai.tools.base_tool import Tool from crewai.utilities import I18N, Logger, RPMController @@ -48,6 +50,8 @@ class BaseAgent(ABC, BaseModel): cache_handler (InstanceOf[CacheHandler]): An instance of the CacheHandler class. tools_handler (InstanceOf[ToolsHandler]): An instance of the ToolsHandler class. max_tokens: Maximum number of tokens for the agent to generate in a response. + knowledge_sources: Knowledge sources for the agent. + knowledge_storage: Custom knowledge storage for the agent. Methods: @@ -130,6 +134,17 @@ class BaseAgent(ABC, BaseModel): max_tokens: Optional[int] = Field( default=None, description="Maximum number of tokens for the agent's execution." ) + knowledge: Optional[Knowledge] = Field( + default=None, description="Knowledge for the agent." + ) + knowledge_sources: Optional[List[BaseKnowledgeSource]] = Field( + default=None, + description="Knowledge sources for the agent.", + ) + knowledge_storage: Optional[Any] = Field( + default=None, + description="Custom knowledge storage for the agent.", + ) @model_validator(mode="before") @classmethod @@ -256,13 +271,44 @@ class BaseAgent(ABC, BaseModel): "tools_handler", "cache_handler", "llm", + "knowledge_sources", + "knowledge_storage", + "knowledge", } - # Copy llm and clear callbacks + # Copy llm existing_llm = shallow_copy(self.llm) + copied_knowledge = shallow_copy(self.knowledge) + copied_knowledge_storage = shallow_copy(self.knowledge_storage) + # Properly copy knowledge sources if they exist + existing_knowledge_sources = None + if self.knowledge_sources: + # Create a shared storage instance for all knowledge sources + shared_storage = ( + self.knowledge_sources[0].storage if self.knowledge_sources else None + ) + + existing_knowledge_sources = [] + for source in self.knowledge_sources: + copied_source = ( + source.model_copy() + if hasattr(source, "model_copy") + else shallow_copy(source) + ) + # Ensure all copied sources use the same storage instance + copied_source.storage = shared_storage + existing_knowledge_sources.append(copied_source) + copied_data = self.model_dump(exclude=exclude) copied_data = {k: v for k, v in copied_data.items() if v is not None} - copied_agent = type(self)(**copied_data, llm=existing_llm, tools=self.tools) + copied_agent = type(self)( + **copied_data, + llm=existing_llm, + tools=self.tools, + knowledge_sources=existing_knowledge_sources, + knowledge=copied_knowledge, + knowledge_storage=copied_knowledge_storage, + ) return copied_agent diff --git a/src/crewai/crew.py b/src/crewai/crew.py index 5d4b9ff79..b44667042 100644 --- a/src/crewai/crew.py +++ b/src/crewai/crew.py @@ -4,6 +4,7 @@ import re import uuid import warnings from concurrent.futures import Future +from copy import copy as shallow_copy from hashlib import md5 from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union @@ -210,8 +211,9 @@ class Crew(BaseModel): default=None, description="LLM used to handle chatting with the crew.", ) - _knowledge: Optional[Knowledge] = PrivateAttr( + knowledge: Optional[Knowledge] = Field( default=None, + description="Knowledge for the crew.", ) @field_validator("id", mode="before") @@ -289,7 +291,7 @@ class Crew(BaseModel): if isinstance(self.knowledge_sources, list) and all( isinstance(k, BaseKnowledgeSource) for k in self.knowledge_sources ): - self._knowledge = Knowledge( + self.knowledge = Knowledge( sources=self.knowledge_sources, embedder_config=self.embedder, collection_name="crew", @@ -991,8 +993,8 @@ class Crew(BaseModel): return result def query_knowledge(self, query: List[str]) -> Union[List[Dict[str, Any]], None]: - if self._knowledge: - return self._knowledge.query(query) + if self.knowledge: + return self.knowledge.query(query) return None def fetch_inputs(self) -> Set[str]: @@ -1036,6 +1038,8 @@ class Crew(BaseModel): "_telemetry", "agents", "tasks", + "knowledge_sources", + "knowledge", } cloned_agents = [agent.copy() for agent in self.agents] @@ -1043,6 +1047,9 @@ class Crew(BaseModel): task_mapping = {} cloned_tasks = [] + existing_knowledge_sources = shallow_copy(self.knowledge_sources) + existing_knowledge = shallow_copy(self.knowledge) + for task in self.tasks: cloned_task = task.copy(cloned_agents, task_mapping) cloned_tasks.append(cloned_task) @@ -1062,7 +1069,13 @@ class Crew(BaseModel): copied_data.pop("agents", None) copied_data.pop("tasks", None) - copied_crew = Crew(**copied_data, agents=cloned_agents, tasks=cloned_tasks) + copied_crew = Crew( + **copied_data, + agents=cloned_agents, + tasks=cloned_tasks, + knowledge_sources=existing_knowledge_sources, + knowledge=existing_knowledge, + ) return copied_crew diff --git a/src/crewai/knowledge/knowledge.py b/src/crewai/knowledge/knowledge.py index c964333c8..d1d4ede6c 100644 --- a/src/crewai/knowledge/knowledge.py +++ b/src/crewai/knowledge/knowledge.py @@ -15,20 +15,20 @@ class Knowledge(BaseModel): Args: sources: List[BaseKnowledgeSource] = Field(default_factory=list) storage: Optional[KnowledgeStorage] = Field(default=None) - embedder_config: Optional[Dict[str, Any]] = None + embedder: Optional[Dict[str, Any]] = None """ sources: List[BaseKnowledgeSource] = Field(default_factory=list) model_config = ConfigDict(arbitrary_types_allowed=True) storage: Optional[KnowledgeStorage] = Field(default=None) - embedder_config: Optional[Dict[str, Any]] = None + embedder: Optional[Dict[str, Any]] = None collection_name: Optional[str] = None def __init__( self, collection_name: str, sources: List[BaseKnowledgeSource], - embedder_config: Optional[Dict[str, Any]] = None, + embedder: Optional[Dict[str, Any]] = None, storage: Optional[KnowledgeStorage] = None, **data, ): @@ -37,25 +37,23 @@ class Knowledge(BaseModel): self.storage = storage else: self.storage = KnowledgeStorage( - embedder_config=embedder_config, collection_name=collection_name + embedder=embedder, collection_name=collection_name ) self.sources = sources self.storage.initialize_knowledge_storage() - for source in sources: - source.storage = self.storage - source.add() + self._add_sources() def query(self, query: List[str], limit: int = 3) -> List[Dict[str, Any]]: """ Query across all knowledge sources to find the most relevant information. Returns the top_k most relevant chunks. - + Raises: ValueError: If storage is not initialized. """ if self.storage is None: raise ValueError("Storage is not initialized.") - + results = self.storage.search( query, limit, @@ -63,6 +61,9 @@ class Knowledge(BaseModel): return results def _add_sources(self): - for source in self.sources: - source.storage = self.storage - source.add() + try: + for source in self.sources: + source.storage = self.storage + source.add() + except Exception as e: + raise e diff --git a/src/crewai/knowledge/source/base_file_knowledge_source.py b/src/crewai/knowledge/source/base_file_knowledge_source.py index ac345b6a6..4c4b9b337 100644 --- a/src/crewai/knowledge/source/base_file_knowledge_source.py +++ b/src/crewai/knowledge/source/base_file_knowledge_source.py @@ -29,7 +29,13 @@ class BaseFileKnowledgeSource(BaseKnowledgeSource, ABC): def validate_file_path(cls, v, info): """Validate that at least one of file_path or file_paths is provided.""" # Single check if both are None, O(1) instead of nested conditions - if v is None and info.data.get("file_path" if info.field_name == "file_paths" else "file_paths") is None: + if ( + v is None + and info.data.get( + "file_path" if info.field_name == "file_paths" else "file_paths" + ) + is None + ): raise ValueError("Either file_path or file_paths must be provided") return v diff --git a/src/crewai/knowledge/storage/knowledge_storage.py b/src/crewai/knowledge/storage/knowledge_storage.py index 4a70c5997..9e6ab8041 100644 --- a/src/crewai/knowledge/storage/knowledge_storage.py +++ b/src/crewai/knowledge/storage/knowledge_storage.py @@ -48,11 +48,11 @@ class KnowledgeStorage(BaseKnowledgeStorage): def __init__( self, - embedder_config: Optional[Dict[str, Any]] = None, + embedder: Optional[Dict[str, Any]] = None, collection_name: Optional[str] = None, ): self.collection_name = collection_name - self._set_embedder_config(embedder_config) + self._set_embedder_config(embedder) def search( self, @@ -99,7 +99,7 @@ class KnowledgeStorage(BaseKnowledgeStorage): ) if self.app: self.collection = self.app.get_or_create_collection( - name=collection_name, embedding_function=self.embedder_config + name=collection_name, embedding_function=self.embedder ) else: raise Exception("Vector Database Client not initialized") @@ -187,17 +187,15 @@ class KnowledgeStorage(BaseKnowledgeStorage): api_key=os.getenv("OPENAI_API_KEY"), model_name="text-embedding-3-small" ) - def _set_embedder_config( - self, embedder_config: 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. Args: embedder_config (Optional[Dict[str, Any]]): Configuration dictionary for the embedder. If None or empty, defaults to the default embedding function. """ - self.embedder_config = ( - EmbeddingConfigurator().configure_embedder(embedder_config) - if embedder_config + self.embedder = ( + EmbeddingConfigurator().configure_embedder(embedder) + if embedder else self._create_default_embedding_function() ) diff --git a/src/crewai/utilities/embedding_configurator.py b/src/crewai/utilities/embedding_configurator.py index 71965bf53..875dab977 100644 --- a/src/crewai/utilities/embedding_configurator.py +++ b/src/crewai/utilities/embedding_configurator.py @@ -43,7 +43,6 @@ class EmbeddingConfigurator: raise Exception( f"Unsupported embedding provider: {provider}, supported providers: {list(self.embedding_functions.keys())}" ) - return self.embedding_functions[provider](config, model_name) @staticmethod diff --git a/tests/agent_test.py b/tests/agent_test.py index fda47daaf..b0efef82b 100644 --- a/tests/agent_test.py +++ b/tests/agent_test.py @@ -10,13 +10,14 @@ from crewai import Agent, Crew, Task from crewai.agents.cache import CacheHandler from crewai.agents.crew_agent_executor import CrewAgentExecutor from crewai.agents.parser import AgentAction, CrewAgentParser, OutputParserException +from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource from crewai.llm import LLM from crewai.tools import tool from crewai.tools.tool_calling import InstructorToolCalling from crewai.tools.tool_usage import ToolUsage from crewai.tools.tool_usage_events import ToolUsageFinished -from crewai.utilities import Printer, RPMController +from crewai.utilities import RPMController from crewai.utilities.events import Emitter @@ -1602,6 +1603,45 @@ def test_agent_with_knowledge_sources(): assert "red" in result.raw.lower() +@pytest.mark.vcr(filter_headers=["authorization"]) +def test_agent_with_knowledge_sources_works_with_copy(): + content = "Brandon's favorite color is red and he likes Mexican food." + string_source = StringKnowledgeSource(content=content) + + with patch( + "crewai.knowledge.source.base_knowledge_source.BaseKnowledgeSource", + autospec=True, + ) as MockKnowledgeSource: + mock_knowledge_source_instance = MockKnowledgeSource.return_value + mock_knowledge_source_instance.__class__ = BaseKnowledgeSource + mock_knowledge_source_instance.sources = [string_source] + + agent = Agent( + role="Information Agent", + goal="Provide information based on knowledge sources", + backstory="You have access to specific knowledge sources.", + llm=LLM(model="gpt-4o-mini"), + knowledge_sources=[string_source], + ) + + with patch( + "crewai.knowledge.storage.knowledge_storage.KnowledgeStorage" + ) as MockKnowledgeStorage: + mock_knowledge_storage = MockKnowledgeStorage.return_value + agent.knowledge_storage = mock_knowledge_storage + + agent_copy = agent.copy() + + assert agent_copy.role == agent.role + assert agent_copy.goal == agent.goal + assert agent_copy.backstory == agent.backstory + assert agent_copy.knowledge_sources is not None + assert len(agent_copy.knowledge_sources) == 1 + assert isinstance(agent_copy.knowledge_sources[0], StringKnowledgeSource) + assert agent_copy.knowledge_sources[0].content == content + assert isinstance(agent_copy.llm, LLM) + + @pytest.mark.vcr(filter_headers=["authorization"]) def test_litellm_auth_error_handling(): """Test that LiteLLM authentication errors are handled correctly and not retried.""" diff --git a/tests/cassettes/test_agent_with_knowledge_sources_works_with_copy.yaml b/tests/cassettes/test_agent_with_knowledge_sources_works_with_copy.yaml new file mode 100644 index 000000000..176be39c8 --- /dev/null +++ b/tests/cassettes/test_agent_with_knowledge_sources_works_with_copy.yaml @@ -0,0 +1,206 @@ +interactions: +- request: + body: '{"input": ["Brandon''s favorite color is red and he likes Mexican food."], + "model": "text-embedding-3-small", "encoding_format": "base64"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '137' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.59.6 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.59.6 + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.8 + method: POST + uri: https://api.openai.com/v1/embeddings + response: + content: "{\n \"object\": \"list\",\n \"data\": [\n {\n \"object\": + \"embedding\",\n \"index\": 0,\n \"embedding\": \"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\"\n + \ }\n ],\n \"model\": \"text-embedding-3-small\",\n \"usage\": {\n \"prompt_tokens\": + 12,\n \"total_tokens\": 12\n }\n}\n" + headers: + CF-RAY: + - 908b749c8cb41576-SJC + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Mon, 27 Jan 2025 20:22:34 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=NhRx2kcSiBEOhkZbWaKlY_pw46LGzb7BpUNF.ozrJrY-1738009354-1.0.1.1-naI_MYI5l4_BbeD3mwpu.Pi55FVDn3ImnfFjreNp0bbAvTuf8xOJY8HgxhE.W4XWbq247SbevyoE9aStMYq0ow; + path=/; expires=Mon, 27-Jan-25 20:52:34 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=xnfGIFZVE6LqgVkRMk6ORXsMurOmTu.z7TTz7afn810-1738009354083-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-allow-origin: + - '*' + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + openai-model: + - text-embedding-3-small + openai-organization: + - crewai-iuxna1 + openai-processing-ms: + - '75' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + via: + - envoy-router-75f99bb574-mb9tb + x-envoy-upstream-service-time: + - '29' + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '10000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '9999986' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_4e3d0c147826a183e2848ca1df2c9da9 + http_version: HTTP/1.1 + status_code: 200 +- request: + body: '{"input": ["Brandon''s favorite color is red and he likes Mexican food."], + "model": "text-embedding-3-small", "encoding_format": "base64"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '137' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.59.6 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.59.6 + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.8 + method: POST + uri: https://api.openai.com/v1/embeddings + response: + content: "{\n \"object\": \"list\",\n \"data\": [\n {\n \"object\": + \"embedding\",\n \"index\": 0,\n \"embedding\": \"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\"\n + \ }\n ],\n \"model\": \"text-embedding-3-small\",\n \"usage\": {\n \"prompt_tokens\": + 12,\n \"total_tokens\": 12\n }\n}\n" + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 908b749fcdbaed36-SJC + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Mon, 27 Jan 2025 20:22:34 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=hTW9TNu3pB35yAIOgg3sdy1hLtP_un1Js4.ZfsmNEXY-1738009354-1.0.1.1-pmAOhPxdO75O.Xt22Tnz_8pitmTMJY.vDeWPxXlJq3TTay0D.285FuCezcz8iy6gLi0hF9SRUc5f5xovdsaQOA; + path=/; expires=Mon, 27-Jan-25 20:52:34 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=KXf4AO65W0FpWKL_jL5Tw4Xdts32F1mkwYcniiqUZtU-1738009354603-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-allow-origin: + - '*' + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + openai-model: + - text-embedding-3-small + openai-organization: + - crewai-iuxna1 + openai-processing-ms: + - '113' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + via: + - envoy-router-5cc9fb545f-x4k6f + x-envoy-upstream-service-time: + - '74' + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '10000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '9999986' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_7b9c56b5c3be975b8ce088f3457a52f9 + http_version: HTTP/1.1 + status_code: 200 +version: 1 diff --git a/tests/cassettes/test_crew_with_knowledge_sources_works_with_copy.yaml b/tests/cassettes/test_crew_with_knowledge_sources_works_with_copy.yaml new file mode 100644 index 000000000..c344ed7bd --- /dev/null +++ b/tests/cassettes/test_crew_with_knowledge_sources_works_with_copy.yaml @@ -0,0 +1,206 @@ +interactions: +- request: + body: '{"input": ["Brandon''s favorite color is red and he likes Mexican food."], + "model": "text-embedding-3-small", "encoding_format": "base64"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '137' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.59.6 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.59.6 + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.8 + method: POST + uri: https://api.openai.com/v1/embeddings + response: + content: "{\n \"object\": \"list\",\n \"data\": [\n {\n \"object\": + \"embedding\",\n \"index\": 0,\n \"embedding\": \"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\"\n + \ }\n ],\n \"model\": \"text-embedding-3-small\",\n \"usage\": {\n \"prompt_tokens\": + 12,\n \"total_tokens\": 12\n }\n}\n" + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 9072bf7c2a5a2368-SJC + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Fri, 24 Jan 2025 20:24:36 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=QK.uKShFHIukTlLKV8KaH39RKmf.DA9fbdIp_5JPWp0-1737750276-1.0.1.1-U7TK7a58ic2LWeNf6OwFzCGWgz2X06RW7R0O0mr8QRYXDoZzLKeG_c1nzqrtBldVwPNYiThnXzesVGG6xXiXSA; + path=/; expires=Fri, 24-Jan-25 20:54:36 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=uqbfBV84.dehSGRMLX4tXE1mi3miPlWZSPvMxI8q.9g-1737750276975-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-allow-origin: + - '*' + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + openai-model: + - text-embedding-3-small + openai-organization: + - crewai-iuxna1 + openai-processing-ms: + - '136' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + via: + - envoy-router-5985cc59bb-fvqpf + x-envoy-upstream-service-time: + - '46' + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '10000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '9999986' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_3983df28a40cce518f5a800922e01028 + http_version: HTTP/1.1 + status_code: 200 +- request: + body: '{"input": ["Brandon''s favorite color is red and he likes Mexican food."], + "model": "text-embedding-3-small", "encoding_format": "base64"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '137' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.59.6 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 1.59.6 + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.8 + method: POST + uri: https://api.openai.com/v1/embeddings + response: + content: "{\n \"object\": \"list\",\n \"data\": [\n {\n \"object\": + \"embedding\",\n \"index\": 0,\n \"embedding\": \"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\"\n + \ }\n ],\n \"model\": \"text-embedding-3-small\",\n \"usage\": {\n \"prompt_tokens\": + 12,\n \"total_tokens\": 12\n }\n}\n" + headers: + CF-Cache-Status: + - DYNAMIC + CF-RAY: + - 9072bf803bed7ae0-SJC + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Fri, 24 Jan 2025 20:24:38 GMT + Server: + - cloudflare + Set-Cookie: + - __cf_bm=jtzsU7lWc3d6B4KQxyjgxkttPzNPU8tMxA_s1vkjLI4-1737750278-1.0.1.1-GEGJzRKGIhPNMpEUz_Rh1dVq5Pl4.NRVTCurfAC_LMKDRZrKec4U8BF3B7egdjrrjsKssZ8eeHXAr1U7v6O9qQ; + path=/; expires=Fri, 24-Jan-25 20:54:38 GMT; domain=.api.openai.com; HttpOnly; + Secure; SameSite=None + - _cfuvid=iA.YuWEfBSZCELL7i1Nqta1cyNeMLTrl8AqxK0PB6XA-1737750278342-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-allow-origin: + - '*' + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + openai-model: + - text-embedding-3-small + openai-organization: + - crewai-iuxna1 + openai-processing-ms: + - '1090' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + via: + - envoy-router-7c649fddd4-v8m26 + x-envoy-upstream-service-time: + - '1036' + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '10000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '9999986' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_77c48e76b18b892fec2de24815ac2b92 + http_version: HTTP/1.1 + status_code: 200 +version: 1 diff --git a/tests/crew_test.py b/tests/crew_test.py index 2f347a50e..b22bb5ffd 100644 --- a/tests/crew_test.py +++ b/tests/crew_test.py @@ -14,6 +14,7 @@ from crewai.agent import Agent from crewai.agents.cache import CacheHandler from crewai.crew import Crew from crewai.crews.crew_output import CrewOutput +from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource from crewai.memory.contextual.contextual_memory import ContextualMemory from crewai.process import Process from crewai.project import crew @@ -555,12 +556,12 @@ def test_crew_with_delegating_agents_should_not_override_task_tools(): _, kwargs = mock_execute_sync.call_args tools = kwargs["tools"] - assert any( - isinstance(tool, TestTool) for tool in tools - ), "TestTool should be present" - assert any( - "delegate" in tool.name.lower() for tool in tools - ), "Delegation tool should be present" + assert any(isinstance(tool, TestTool) for tool in tools), ( + "TestTool should be present" + ) + assert any("delegate" in tool.name.lower() for tool in tools), ( + "Delegation tool should be present" + ) @pytest.mark.vcr(filter_headers=["authorization"]) @@ -619,12 +620,12 @@ def test_crew_with_delegating_agents_should_not_override_agent_tools(): _, kwargs = mock_execute_sync.call_args tools = kwargs["tools"] - assert any( - isinstance(tool, TestTool) for tool in new_ceo.tools - ), "TestTool should be present" - assert any( - "delegate" in tool.name.lower() for tool in tools - ), "Delegation tool should be present" + assert any(isinstance(tool, TestTool) for tool in new_ceo.tools), ( + "TestTool should be present" + ) + assert any("delegate" in tool.name.lower() for tool in tools), ( + "Delegation tool should be present" + ) @pytest.mark.vcr(filter_headers=["authorization"]) @@ -748,17 +749,17 @@ def test_task_tools_override_agent_tools_with_allow_delegation(): used_tools = kwargs["tools"] # Confirm AnotherTestTool is present but TestTool is not - assert any( - isinstance(tool, AnotherTestTool) for tool in used_tools - ), "AnotherTestTool should be present" - assert not any( - isinstance(tool, TestTool) for tool in used_tools - ), "TestTool should not be present among used tools" + assert any(isinstance(tool, AnotherTestTool) for tool in used_tools), ( + "AnotherTestTool should be present" + ) + assert not any(isinstance(tool, TestTool) for tool in used_tools), ( + "TestTool should not be present among used tools" + ) # Confirm delegation tool(s) are present - assert any( - "delegate" in tool.name.lower() for tool in used_tools - ), "Delegation tool should be present" + assert any("delegate" in tool.name.lower() for tool in used_tools), ( + "Delegation tool should be present" + ) # Finally, make sure the agent's original tools remain unchanged assert len(researcher_with_delegation.tools) == 1 @@ -1466,7 +1467,6 @@ def test_dont_set_agents_step_callback_if_already_set(): @pytest.mark.vcr(filter_headers=["authorization"]) def test_crew_function_calling_llm(): - from crewai import LLM from crewai.tools import tool @@ -1560,9 +1560,9 @@ def test_code_execution_flag_adds_code_tool_upon_kickoff(): # Verify that exactly one tool was used and it was a CodeInterpreterTool assert len(used_tools) == 1, "Should have exactly one tool" - assert isinstance( - used_tools[0], CodeInterpreterTool - ), "Tool should be CodeInterpreterTool" + assert isinstance(used_tools[0], CodeInterpreterTool), ( + "Tool should be CodeInterpreterTool" + ) @pytest.mark.vcr(filter_headers=["authorization"]) @@ -3107,9 +3107,9 @@ def test_fetch_inputs(): expected_placeholders = {"role_detail", "topic", "field"} actual_placeholders = crew.fetch_inputs() - assert ( - actual_placeholders == expected_placeholders - ), f"Expected {expected_placeholders}, but got {actual_placeholders}" + assert actual_placeholders == expected_placeholders, ( + f"Expected {expected_placeholders}, but got {actual_placeholders}" + ) def test_task_tools_preserve_code_execution_tools(): @@ -3182,20 +3182,20 @@ def test_task_tools_preserve_code_execution_tools(): used_tools = kwargs["tools"] # Verify all expected tools are present - assert any( - isinstance(tool, TestTool) for tool in used_tools - ), "Task's TestTool should be present" - assert any( - isinstance(tool, CodeInterpreterTool) for tool in used_tools - ), "CodeInterpreterTool should be present" - assert any( - "delegate" in tool.name.lower() for tool in used_tools - ), "Delegation tool should be present" + assert any(isinstance(tool, TestTool) for tool in used_tools), ( + "Task's TestTool should be present" + ) + assert any(isinstance(tool, CodeInterpreterTool) for tool in used_tools), ( + "CodeInterpreterTool should be present" + ) + assert any("delegate" in tool.name.lower() for tool in used_tools), ( + "Delegation tool should be present" + ) # Verify the total number of tools (TestTool + CodeInterpreter + 2 delegation tools) - assert ( - len(used_tools) == 4 - ), "Should have TestTool, CodeInterpreter, and 2 delegation tools" + assert len(used_tools) == 4, ( + "Should have TestTool, CodeInterpreter, and 2 delegation tools" + ) @pytest.mark.vcr(filter_headers=["authorization"]) @@ -3239,9 +3239,9 @@ def test_multimodal_flag_adds_multimodal_tools(): used_tools = kwargs["tools"] # Check that the multimodal tool was added - assert any( - isinstance(tool, AddImageTool) for tool in used_tools - ), "AddImageTool should be present when agent is multimodal" + assert any(isinstance(tool, AddImageTool) for tool in used_tools), ( + "AddImageTool should be present when agent is multimodal" + ) # Verify we have exactly one tool (just the AddImageTool) assert len(used_tools) == 1, "Should only have the AddImageTool" @@ -3467,9 +3467,9 @@ def test_crew_guardrail_feedback_in_context(): assert len(execution_contexts) > 1, "Task should have been executed multiple times" # Verify that the second execution included the guardrail feedback - assert ( - "Output must contain the keyword 'IMPORTANT'" in execution_contexts[1] - ), "Guardrail feedback should be included in retry context" + assert "Output must contain the keyword 'IMPORTANT'" in execution_contexts[1], ( + "Guardrail feedback should be included in retry context" + ) # Verify final output meets guardrail requirements assert "IMPORTANT" in result.raw, "Final output should contain required keyword" @@ -3494,7 +3494,6 @@ def test_before_kickoff_callback(): @before_kickoff def modify_inputs(self, inputs): - self.inputs_modified = True inputs["modified"] = True return inputs @@ -3596,3 +3595,21 @@ def test_before_kickoff_without_inputs(): # Verify that the inputs were initialized and modified inside the before_kickoff method assert test_crew_instance.received_inputs is not None assert test_crew_instance.received_inputs.get("modified") is True + + +@pytest.mark.vcr(filter_headers=["authorization"]) +def test_crew_with_knowledge_sources_works_with_copy(): + content = "Brandon's favorite color is red and he likes Mexican food." + string_source = StringKnowledgeSource(content=content) + + crew = Crew( + agents=[researcher, writer], + tasks=[Task(description="test", expected_output="test", agent=researcher)], + knowledge_sources=[string_source], + ) + + crew_copy = crew.copy() + + assert crew_copy.knowledge_sources == crew.knowledge_sources + assert len(crew_copy.agents) == len(crew.agents) + assert len(crew_copy.tasks) == len(crew.tasks)