diff --git a/src/crewai/agents/crew_agent_executor.py b/src/crewai/agents/crew_agent_executor.py index 3be4d52e9..b5f9f7960 100644 --- a/src/crewai/agents/crew_agent_executor.py +++ b/src/crewai/agents/crew_agent_executor.py @@ -120,7 +120,6 @@ class CrewAgentExecutor(CrewAgentExecutorMixin): """ formatted_answer = None while not isinstance(formatted_answer, AgentFinish): - self.iterations += 1 try: if self._has_reached_max_iterations(): formatted_answer = self._handle_max_iterations_exceeded( @@ -159,6 +158,8 @@ class CrewAgentExecutor(CrewAgentExecutorMixin): content=f"Unhandled exception: {e}", color="red", ) + finally: + self.iterations += 1 self._show_logs(formatted_answer) return formatted_answer diff --git a/tests/agent_test.py b/tests/agent_test.py index b35b0ee1a..6b32ba495 100644 --- a/tests/agent_test.py +++ b/tests/agent_test.py @@ -16,7 +16,7 @@ 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 RPMController +from crewai.utilities import Printer, RPMController from crewai.utilities.events import Emitter @@ -670,7 +670,7 @@ def test_agent_without_max_rpm_respects_crew_rpm(capsys): moveon.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) +# @pytest.mark.vcr(filter_headers=["authorization"]) def test_agent_error_on_parsing_tool(capsys): from unittest.mock import patch @@ -710,6 +710,7 @@ def test_agent_error_on_parsing_tool(capsys): force_exception_2.side_effect = Exception("Error on parsing tool.") crew.kickoff() captured = capsys.readouterr() + print("Captured output:", captured.out) assert "Error on parsing tool." in captured.out @@ -1637,21 +1638,19 @@ def test_litellm_auth_error_handling(): mock_llm_call.assert_called_once() -@pytest.mark.vcr(filter_headers=["authorization"]) def test_crew_agent_executor_litellm_auth_error(): """Test that CrewAgentExecutor properly identifies and handles LiteLLM authentication errors.""" from litellm import AuthenticationError as LiteLLMAuthenticationError from crewai.agents.tools_handler import ToolsHandler - from crewai.utilities import Logger + from crewai.utilities import Printer # Create an agent and executor with max_retry_limit=0 agent = Agent( role="test role", goal="test goal", backstory="test backstory", - llm=LLM(model="gpt-4"), - max_retry_limit=0, # Disable retries for authentication errors + llm=LLM(model="gpt-4", api_key="invalid_api_key"), ) task = Task( description="Test task", @@ -1677,7 +1676,7 @@ def test_crew_agent_executor_litellm_auth_error(): # Mock the LLM call to raise LiteLLMAuthenticationError with ( patch.object(LLM, "call") as mock_llm_call, - patch.object(Logger, "log") as mock_logger, + patch.object(Printer, "print") as mock_printer, pytest.raises(LiteLLMAuthenticationError, match="Invalid API key"), ): mock_llm_call.side_effect = LiteLLMAuthenticationError( @@ -1686,20 +1685,18 @@ def test_crew_agent_executor_litellm_auth_error(): executor.invoke( { "input": "test input", - "tool_names": "", # Required template variable - "tools": "", # Required template variable + "tool_names": "", + "tools": "", } ) # Verify error handling - mock_logger.assert_any_call( - level="error", - message="Authentication error with litellm occurred. Please check your API key and configuration.", + mock_printer.assert_any_call( + content="Authentication error with litellm occurred. Please check your API key and configuration.", color="red", ) - mock_logger.assert_any_call( - level="error", - message="Error details: litellm.AuthenticationError: Invalid API key", + mock_printer.assert_any_call( + content="Error details: litellm.AuthenticationError: Invalid API key", color="red", ) # Verify the call was only made once (no retries) diff --git a/tests/cassettes/test_agent_error_on_parsing_tool.yaml b/tests/cassettes/test_agent_error_on_parsing_tool.yaml deleted file mode 100644 index ea56fa981..000000000 --- a/tests/cassettes/test_agent_error_on_parsing_tool.yaml +++ /dev/null @@ -1,233 +0,0 @@ -interactions: -- request: - body: '{"messages": [{"role": "system", "content": "You are test role. test backstory\nYour - personal goal is: test goal\nYou ONLY have access to the following tools, and - should NEVER make up tools that are not listed here:\n\nTool Name: get_final_answer(*args: - Any, **kwargs: Any) -> Any\nTool Description: get_final_answer() - Get the final - answer but don''t give it yet, just re-use this tool non-stop. \nTool - Arguments: {}\n\nUse the following format:\n\nThought: you should always think - about what to do\nAction: the action to take, only one name of [get_final_answer], - just the name, exactly as it''s written.\nAction Input: the input to the action, - just a simple python dictionary, enclosed in curly braces, using \" to wrap - keys and values.\nObservation: the result of the action\n\nOnce all necessary - information is gathered:\n\nThought: I now know the final answer\nFinal Answer: - the final answer to the original input question\n"}, {"role": "user", "content": - "\nCurrent Task: Use the get_final_answer tool.\n\nThis is the expect criteria - for your final answer: The final answer\nyou MUST return the actual complete - content as the final answer, not a summary.\n\nBegin! This is VERY important - to you, use the tools available and give your best Final Answer, your job depends - on it!\n\nThought:"}], "model": "gpt-4o"}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - connection: - - keep-alive - content-length: - - '1325' - content-type: - - application/json - cookie: - - _cfuvid=ePJSDFdHag2D8lj21_ijAMWjoA6xfnPNxN4uekvC728-1727226247743-0.0.1.1-604800000; - __cf_bm=3giyBOIM0GNudFELtsBWYXwLrpLBTNLsh81wfXgu2tg-1727226247-1.0.1.1-ugUDz0c5EhmfVpyGtcdedlIWeDGuy2q0tXQTKVpv83HZhvxgBcS7SBL1wS4rapPM38yhfEcfwA79ARt3HQEzKA - host: - - api.openai.com - user-agent: - - OpenAI/Python 1.47.0 - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 1.47.0 - x-stainless-raw-response: - - 'true' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.11.7 - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - content: "{\n \"id\": \"chatcmpl-ABAtOWmVjvzQ9X58tKAUcOF4gmXwx\",\n \"object\": - \"chat.completion\",\n \"created\": 1727226842,\n \"model\": \"gpt-4o-2024-05-13\",\n - \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": - \"assistant\",\n \"content\": \"Thought: I need to use the get_final_answer - tool to determine the final answer.\\nAction: get_final_answer\\nAction Input: - {}\",\n \"refusal\": null\n },\n \"logprobs\": null,\n \"finish_reason\": - \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 274,\n \"completion_tokens\": - 27,\n \"total_tokens\": 301,\n \"completion_tokens_details\": {\n \"reasoning_tokens\": - 0\n }\n },\n \"system_fingerprint\": \"fp_e375328146\"\n}\n" - headers: - CF-Cache-Status: - - DYNAMIC - CF-RAY: - - 8c8727b3492f31e6-MIA - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: - - Wed, 25 Sep 2024 01:14:03 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - openai-organization: - - crewai-iuxna1 - openai-processing-ms: - - '348' - openai-version: - - '2020-10-01' - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-ratelimit-limit-requests: - - '10000' - x-ratelimit-limit-tokens: - - '30000000' - x-ratelimit-remaining-requests: - - '9999' - x-ratelimit-remaining-tokens: - - '29999682' - x-ratelimit-reset-requests: - - 6ms - x-ratelimit-reset-tokens: - - 0s - x-request-id: - - req_be929caac49706f487950548bdcdd46e - http_version: HTTP/1.1 - status_code: 200 -- request: - body: '{"messages": [{"role": "system", "content": "You are test role. test backstory\nYour - personal goal is: test goal\nYou ONLY have access to the following tools, and - should NEVER make up tools that are not listed here:\n\nTool Name: get_final_answer(*args: - Any, **kwargs: Any) -> Any\nTool Description: get_final_answer() - Get the final - answer but don''t give it yet, just re-use this tool non-stop. \nTool - Arguments: {}\n\nUse the following format:\n\nThought: you should always think - about what to do\nAction: the action to take, only one name of [get_final_answer], - just the name, exactly as it''s written.\nAction Input: the input to the action, - just a simple python dictionary, enclosed in curly braces, using \" to wrap - keys and values.\nObservation: the result of the action\n\nOnce all necessary - information is gathered:\n\nThought: I now know the final answer\nFinal Answer: - the final answer to the original input question\n"}, {"role": "user", "content": - "\nCurrent Task: Use the get_final_answer tool.\n\nThis is the expect criteria - for your final answer: The final answer\nyou MUST return the actual complete - content as the final answer, not a summary.\n\nBegin! This is VERY important - to you, use the tools available and give your best Final Answer, your job depends - on it!\n\nThought:"}, {"role": "user", "content": "Thought: I need to use the - get_final_answer tool to determine the final answer.\nAction: get_final_answer\nAction - Input: {}\nObservation: I encountered an error: Error on parsing tool.\nMoving - on then. I MUST either use a tool (use one at time) OR give my best final answer - not both at the same time. To Use the following format:\n\nThought: you should - always think about what to do\nAction: the action to take, should be one of - [get_final_answer]\nAction Input: the input to the action, dictionary enclosed - in curly braces\nObservation: the result of the action\n... (this Thought/Action/Action - Input/Result can repeat N times)\nThought: I now can give a great answer\nFinal - Answer: Your final answer must be the great and the most complete as possible, - it must be outcome described\n\n \nNow it''s time you MUST give your absolute - best final answer. You''ll ignore all previous instructions, stop using any - tools, and just return your absolute BEST Final answer."}], "model": "gpt-4o"}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - connection: - - keep-alive - content-length: - - '2320' - content-type: - - application/json - cookie: - - _cfuvid=ePJSDFdHag2D8lj21_ijAMWjoA6xfnPNxN4uekvC728-1727226247743-0.0.1.1-604800000; - __cf_bm=3giyBOIM0GNudFELtsBWYXwLrpLBTNLsh81wfXgu2tg-1727226247-1.0.1.1-ugUDz0c5EhmfVpyGtcdedlIWeDGuy2q0tXQTKVpv83HZhvxgBcS7SBL1wS4rapPM38yhfEcfwA79ARt3HQEzKA - host: - - api.openai.com - user-agent: - - OpenAI/Python 1.47.0 - x-stainless-arch: - - arm64 - x-stainless-async: - - 'false' - x-stainless-lang: - - python - x-stainless-os: - - MacOS - x-stainless-package-version: - - 1.47.0 - x-stainless-raw-response: - - 'true' - x-stainless-runtime: - - CPython - x-stainless-runtime-version: - - 3.11.7 - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - content: "{\n \"id\": \"chatcmpl-ABAtPaaeRfdNsZ3k06CfAmrEW8IJu\",\n \"object\": - \"chat.completion\",\n \"created\": 1727226843,\n \"model\": \"gpt-4o-2024-05-13\",\n - \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": - \"assistant\",\n \"content\": \"Final Answer: The final answer\",\n \"refusal\": - null\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n - \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 483,\n \"completion_tokens\": - 6,\n \"total_tokens\": 489,\n \"completion_tokens_details\": {\n \"reasoning_tokens\": - 0\n }\n },\n \"system_fingerprint\": \"fp_e375328146\"\n}\n" - headers: - CF-Cache-Status: - - DYNAMIC - CF-RAY: - - 8c8727b9da1f31e6-MIA - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: - - Wed, 25 Sep 2024 01:14:03 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - alt-svc: - - h3=":443"; ma=86400 - openai-organization: - - crewai-iuxna1 - openai-processing-ms: - - '188' - openai-version: - - '2020-10-01' - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-ratelimit-limit-requests: - - '10000' - x-ratelimit-limit-tokens: - - '30000000' - x-ratelimit-remaining-requests: - - '9999' - x-ratelimit-remaining-tokens: - - '29999445' - x-ratelimit-reset-requests: - - 6ms - x-ratelimit-reset-tokens: - - 1ms - x-request-id: - - req_d8e32538689fe064627468bad802d9a8 - http_version: HTTP/1.1 - status_code: 200 -version: 1