From bdfc38ba321bd5670d50871d51e9ae49775e03e5 Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Wed, 3 Sep 2025 18:18:05 -0400 Subject: [PATCH] refactor: update CacheHandler imports to use direct path - Update imports from crewai.agents.cache to crewai.agents.cache.cache_handler - Remove CacheHandler from agents module __all__ export --- src/crewai/agent.py | 2 +- src/crewai/agents/__init__.py | 9 +- src/crewai/crew.py | 2 +- src/crewai/lite_agent.py | 2 +- src/crewai/tools/cache_tools/cache_tools.py | 2 +- src/crewai/tools/tool_usage.py | 42 ++--- tests/agents/test_agent.py | 14 +- tests/test_crew.py | 170 ++++++++++---------- 8 files changed, 124 insertions(+), 119 deletions(-) diff --git a/src/crewai/agent.py b/src/crewai/agent.py index 122ddc82f..c8019adb1 100644 --- a/src/crewai/agent.py +++ b/src/crewai/agent.py @@ -16,7 +16,7 @@ from typing import ( from pydantic import Field, InstanceOf, PrivateAttr, model_validator -from crewai.agents import CacheHandler +from crewai.agents.cache.cache_handler import CacheHandler from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.agents.crew_agent_executor import CrewAgentExecutor from crewai.knowledge.knowledge import Knowledge diff --git a/src/crewai/agents/__init__.py b/src/crewai/agents/__init__.py index 9e400098c..b7ce5147e 100644 --- a/src/crewai/agents/__init__.py +++ b/src/crewai/agents/__init__.py @@ -1,5 +1,10 @@ -from crewai.agents.cache.cache_handler import CacheHandler from crewai.agents.parser import parse, AgentAction, AgentFinish, OutputParserException from crewai.agents.tools_handler import ToolsHandler -__all__ = ["CacheHandler", "parse", "AgentAction", "AgentFinish", "OutputParserException", "ToolsHandler"] +__all__ = [ + "parse", + "AgentAction", + "AgentFinish", + "OutputParserException", + "ToolsHandler", +] diff --git a/src/crewai/crew.py b/src/crewai/crew.py index 9185d143d..19a8784ae 100644 --- a/src/crewai/crew.py +++ b/src/crewai/crew.py @@ -37,7 +37,7 @@ from pydantic_core import PydanticCustomError from crewai.agent import Agent from crewai.agents.agent_builder.base_agent import BaseAgent -from crewai.agents.cache import CacheHandler +from crewai.agents.cache.cache_handler import CacheHandler from crewai.crews.crew_output import CrewOutput from crewai.flow.flow_trackable import FlowTrackable from crewai.knowledge.knowledge import Knowledge diff --git a/src/crewai/lite_agent.py b/src/crewai/lite_agent.py index d1c9e1dc1..2c4df611e 100644 --- a/src/crewai/lite_agent.py +++ b/src/crewai/lite_agent.py @@ -33,7 +33,7 @@ from pydantic import ( from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess -from crewai.agents.cache import CacheHandler +from crewai.agents.cache.cache_handler import CacheHandler from crewai.agents.parser import ( AgentAction, AgentFinish, diff --git a/src/crewai/tools/cache_tools/cache_tools.py b/src/crewai/tools/cache_tools/cache_tools.py index a81ce98cf..e391d4289 100644 --- a/src/crewai/tools/cache_tools/cache_tools.py +++ b/src/crewai/tools/cache_tools/cache_tools.py @@ -1,6 +1,6 @@ from pydantic import BaseModel, Field -from crewai.agents.cache import CacheHandler +from crewai.agents.cache.cache_handler import CacheHandler from crewai.tools.structured_tool import CrewStructuredTool diff --git a/src/crewai/tools/tool_usage.py b/src/crewai/tools/tool_usage.py index 55d41cd12..414f975c5 100644 --- a/src/crewai/tools/tool_usage.py +++ b/src/crewai/tools/tool_usage.py @@ -11,6 +11,14 @@ import json5 from json_repair import repair_json from crewai.agents.tools_handler import ToolsHandler +from crewai.events.event_bus import crewai_event_bus +from crewai.events.types.tool_usage_events import ( + ToolSelectionErrorEvent, + ToolUsageErrorEvent, + ToolUsageFinishedEvent, + ToolUsageStartedEvent, + ToolValidateInputErrorEvent, +) from crewai.task import Task from crewai.telemetry import Telemetry from crewai.tools.structured_tool import CrewStructuredTool @@ -20,14 +28,6 @@ from crewai.utilities.agent_utils import ( get_tool_names, render_text_description_and_args, ) -from crewai.events.event_bus import crewai_event_bus -from crewai.events.types.tool_usage_events import ( - ToolSelectionErrorEvent, - ToolUsageErrorEvent, - ToolUsageFinishedEvent, - ToolUsageStartedEvent, - ToolValidateInputErrorEvent, -) if TYPE_CHECKING: from crewai.agents.agent_builder.base_agent import BaseAgent @@ -69,12 +69,12 @@ class ToolUsage: def __init__( self, tools_handler: Optional[ToolsHandler], - tools: List[CrewStructuredTool], + tools: list[CrewStructuredTool], task: Optional[Task], function_calling_llm: Any, agent: Optional[Union["BaseAgent", "LiteAgent"]] = None, action: Any = None, - fingerprint_context: Optional[Dict[str, str]] = None, + fingerprint_context: Optional[dict[str, str]] = None, ) -> None: self._i18n: I18N = agent.i18n if agent else I18N() self._printer: Printer = Printer() @@ -105,7 +105,7 @@ class ToolUsage: return self._tool_calling(tool_string) def use( - self, calling: Union[ToolCalling, InstructorToolCalling], tool_string: str + self, calling: ToolCalling | InstructorToolCalling, tool_string: str ) -> str: if isinstance(calling, ToolUsageErrorException): error = calling.message @@ -147,7 +147,7 @@ class ToolUsage: self, tool_string: str, tool: CrewStructuredTool, - calling: Union[ToolCalling, InstructorToolCalling], + calling: ToolCalling | InstructorToolCalling, ) -> str: if self._check_tool_repeated_usage(calling=calling): # type: ignore # _check_tool_repeated_usage of "ToolUsage" does not return a value (it only ever returns None) try: @@ -346,7 +346,7 @@ class ToolUsage: return result def _check_tool_repeated_usage( - self, calling: Union[ToolCalling, InstructorToolCalling] + self, calling: ToolCalling | InstructorToolCalling ) -> bool: if not self.tools_handler: return False @@ -393,7 +393,7 @@ class ToolUsage: return tool if self.task: self.task.increment_tools_errors() - tool_selection_data: Dict[str, Any] = { + tool_selection_data: dict[str, Any] = { "agent_key": getattr(self.agent, "key", None) if self.agent else None, "agent_role": getattr(self.agent, "role", None) if self.agent else None, "tool_name": tool_name, @@ -430,7 +430,7 @@ class ToolUsage: def _function_calling( self, tool_string: str - ) -> Union[ToolCalling, InstructorToolCalling]: + ) -> ToolCalling | InstructorToolCalling: model = ( InstructorToolCalling if self.function_calling_llm.supports_function_calling() @@ -459,7 +459,7 @@ class ToolUsage: def _original_tool_calling( self, tool_string: str, raise_error: bool = False - ) -> Union[ToolCalling, InstructorToolCalling, ToolUsageErrorException]: + ) -> ToolCalling | InstructorToolCalling | ToolUsageErrorException: tool_name = self.action.tool tool = self._select_tool(tool_name) try: @@ -488,7 +488,7 @@ class ToolUsage: def _tool_calling( self, tool_string: str - ) -> Union[ToolCalling, InstructorToolCalling, ToolUsageErrorException]: + ) -> ToolCalling | InstructorToolCalling | ToolUsageErrorException: try: try: return self._original_tool_calling(tool_string, raise_error=True) @@ -510,7 +510,7 @@ class ToolUsage: ) return self._tool_calling(tool_string) - def _validate_tool_input(self, tool_input: Optional[str]) -> Dict[str, Any]: + def _validate_tool_input(self, tool_input: Optional[str]) -> dict[str, Any]: if tool_input is None: return {} @@ -586,7 +586,7 @@ class ToolUsage: def on_tool_error( self, tool: Any, - tool_calling: Union[ToolCalling, InstructorToolCalling], + tool_calling: ToolCalling | InstructorToolCalling, e: Exception, ) -> None: event_data = self._prepare_event_data(tool, tool_calling) @@ -595,7 +595,7 @@ class ToolUsage: def on_tool_use_finished( self, tool: Any, - tool_calling: Union[ToolCalling, InstructorToolCalling], + tool_calling: ToolCalling | InstructorToolCalling, from_cache: bool, started_at: float, result: Any, @@ -616,7 +616,7 @@ class ToolUsage: crewai_event_bus.emit(self, ToolUsageFinishedEvent(**event_data)) def _prepare_event_data( - self, tool: Any, tool_calling: Union[ToolCalling, InstructorToolCalling] + self, tool: Any, tool_calling: ToolCalling | InstructorToolCalling ) -> dict: event_data = { "run_attempts": self._run_attempts, diff --git a/tests/agents/test_agent.py b/tests/agents/test_agent.py index a52888069..9a2121cac 100644 --- a/tests/agents/test_agent.py +++ b/tests/agents/test_agent.py @@ -7,7 +7,7 @@ from unittest.mock import MagicMock, patch import pytest from crewai import Agent, Crew, Task -from crewai.agents.cache import CacheHandler +from crewai.agents.cache.cache_handler import CacheHandler from crewai.agents.crew_agent_executor import AgentFinish, CrewAgentExecutor from crewai.knowledge.knowledge import Knowledge from crewai.knowledge.knowledge_config import KnowledgeConfig @@ -559,9 +559,9 @@ def test_agent_repeated_tool_usage(capsys): expected_message = ( "I tried reusing the same input, I must stop using this action input." ) - assert ( - expected_message in output - ), f"Expected message not found in output. Output was: {output}" + assert expected_message in output, ( + f"Expected message not found in output. Output was: {output}" + ) @pytest.mark.vcr(filter_headers=["authorization"]) @@ -602,9 +602,9 @@ def test_agent_repeated_tool_usage_check_even_with_disabled_cache(capsys): has_max_iterations = "maximum iterations reached" in output_lower has_final_answer = "final answer" in output_lower or "42" in captured.out - assert ( - has_repeated_usage_message or (has_max_iterations and has_final_answer) - ), f"Expected repeated tool usage handling or proper max iteration handling. Output was: {captured.out[:500]}..." + assert has_repeated_usage_message or (has_max_iterations and has_final_answer), ( + f"Expected repeated tool usage handling or proper max iteration handling. Output was: {captured.out[:500]}..." + ) @pytest.mark.vcr(filter_headers=["authorization"]) diff --git a/tests/test_crew.py b/tests/test_crew.py index 14bfa642a..b83c40509 100644 --- a/tests/test_crew.py +++ b/tests/test_crew.py @@ -11,7 +11,7 @@ import pydantic_core import pytest from crewai.agent import Agent -from crewai.agents import CacheHandler +from crewai.agents.cache.cache_handler import CacheHandler from crewai.crew import Crew from crewai.crews.crew_output import CrewOutput from crewai.flow import Flow, start @@ -622,12 +622,12 @@ def test_crew_with_delegating_agents_should_not_override_task_tools(ceo, writer) _, 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"]) @@ -686,12 +686,12 @@ def test_crew_with_delegating_agents_should_not_override_agent_tools(ceo, writer _, 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"]) @@ -815,17 +815,17 @@ def test_task_tools_override_agent_tools_with_allow_delegation(researcher, write 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 @@ -929,9 +929,9 @@ def test_cache_hitting_between_agents(researcher, writer, ceo): tool="multiplier", input={"first_number": 2, "second_number": 6} ) assert cache_calls[0] == expected_call, f"First call mismatch: {cache_calls[0]}" - assert ( - cache_calls[1] == expected_call - ), f"Second call mismatch: {cache_calls[1]}" + assert cache_calls[1] == expected_call, ( + f"Second call mismatch: {cache_calls[1]}" + ) @pytest.mark.vcr(filter_headers=["authorization"]) @@ -1674,9 +1674,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"]) @@ -3815,9 +3815,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(): @@ -3892,20 +3892,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"]) @@ -3949,9 +3949,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" @@ -4215,9 +4215,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" @@ -4433,46 +4433,46 @@ def test_crew_copy_with_memory(): try: crew_copy = crew.copy() - assert hasattr( - crew_copy, "_short_term_memory" - ), "Copied crew should have _short_term_memory" - assert ( - crew_copy._short_term_memory is not None - ), "Copied _short_term_memory should not be None" - assert ( - id(crew_copy._short_term_memory) != original_short_term_id - ), "Copied _short_term_memory should be a new object" + assert hasattr(crew_copy, "_short_term_memory"), ( + "Copied crew should have _short_term_memory" + ) + assert crew_copy._short_term_memory is not None, ( + "Copied _short_term_memory should not be None" + ) + assert id(crew_copy._short_term_memory) != original_short_term_id, ( + "Copied _short_term_memory should be a new object" + ) - assert hasattr( - crew_copy, "_long_term_memory" - ), "Copied crew should have _long_term_memory" - assert ( - crew_copy._long_term_memory is not None - ), "Copied _long_term_memory should not be None" - assert ( - id(crew_copy._long_term_memory) != original_long_term_id - ), "Copied _long_term_memory should be a new object" + assert hasattr(crew_copy, "_long_term_memory"), ( + "Copied crew should have _long_term_memory" + ) + assert crew_copy._long_term_memory is not None, ( + "Copied _long_term_memory should not be None" + ) + assert id(crew_copy._long_term_memory) != original_long_term_id, ( + "Copied _long_term_memory should be a new object" + ) - assert hasattr( - crew_copy, "_entity_memory" - ), "Copied crew should have _entity_memory" - assert ( - crew_copy._entity_memory is not None - ), "Copied _entity_memory should not be None" - assert ( - id(crew_copy._entity_memory) != original_entity_id - ), "Copied _entity_memory should be a new object" + assert hasattr(crew_copy, "_entity_memory"), ( + "Copied crew should have _entity_memory" + ) + assert crew_copy._entity_memory is not None, ( + "Copied _entity_memory should not be None" + ) + assert id(crew_copy._entity_memory) != original_entity_id, ( + "Copied _entity_memory should be a new object" + ) if original_external_id: - assert hasattr( - crew_copy, "_external_memory" - ), "Copied crew should have _external_memory" - assert ( - crew_copy._external_memory is not None - ), "Copied _external_memory should not be None" - assert ( - id(crew_copy._external_memory) != original_external_id - ), "Copied _external_memory should be a new object" + assert hasattr(crew_copy, "_external_memory"), ( + "Copied crew should have _external_memory" + ) + assert crew_copy._external_memory is not None, ( + "Copied _external_memory should not be None" + ) + assert id(crew_copy._external_memory) != original_external_id, ( + "Copied _external_memory should be a new object" + ) else: assert ( not hasattr(crew_copy, "_external_memory")