From 58ba7c5215f08b14dffdc1fe962775457d1c3a03 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Fri, 26 Sep 2025 00:59:21 +0530 Subject: [PATCH 01/18] vo/fix/none_empty_response --- src/crewai/agents/crew_agent_executor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/crewai/agents/crew_agent_executor.py b/src/crewai/agents/crew_agent_executor.py index d912bdf3c1..e52b49ca03 100644 --- a/src/crewai/agents/crew_agent_executor.py +++ b/src/crewai/agents/crew_agent_executor.py @@ -241,9 +241,9 @@ def _invoke_loop(self) -> AgentFinish: if e.__class__.__module__.startswith("litellm"): # Do not retry on litellm errors raise e - if is_context_length_exceeded(e): + if is_context_length_exceeded(e) or (isinstance(e, ValueError) and "None or empty" in str(e)): handle_context_length( - respect_context_window=self.respect_context_window, + respect_context_window=self.respect_context_window if not (isinstance(e, ValueError) and "None or empty" in str(e)) else True, printer=self._printer, messages=self.messages, llm=self.llm, From cfe05b0c635251bbd3a1f4a360a44b82164894c0 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Fri, 26 Sep 2025 19:51:21 +0530 Subject: [PATCH 02/18] Added support for null response check --- src/crewai/agents/crew_agent_executor.py | 5 +++-- src/crewai/utilities/agent_utils.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/crewai/agents/crew_agent_executor.py b/src/crewai/agents/crew_agent_executor.py index e52b49ca03..b15061de10 100644 --- a/src/crewai/agents/crew_agent_executor.py +++ b/src/crewai/agents/crew_agent_executor.py @@ -35,6 +35,7 @@ handle_unknown_error, has_reached_max_iterations, is_context_length_exceeded, + is_null_response_because_context_length_exceeded, process_llm_response, ) from crewai.utilities.constants import TRAINING_DATA_FILE @@ -241,9 +242,9 @@ def _invoke_loop(self) -> AgentFinish: if e.__class__.__module__.startswith("litellm"): # Do not retry on litellm errors raise e - if is_context_length_exceeded(e) or (isinstance(e, ValueError) and "None or empty" in str(e)): + if is_context_length_exceeded(e): handle_context_length( - respect_context_window=self.respect_context_window if not (isinstance(e, ValueError) and "None or empty" in str(e)) else True, + respect_context_window=self.respect_context_window, printer=self._printer, messages=self.messages, llm=self.llm, diff --git a/src/crewai/utilities/agent_utils.py b/src/crewai/utilities/agent_utils.py index 5bc2bcb7fb..f2e0c5208c 100644 --- a/src/crewai/utilities/agent_utils.py +++ b/src/crewai/utilities/agent_utils.py @@ -397,6 +397,29 @@ def is_context_length_exceeded(exception: Exception) -> bool: ) +def is_null_response_because_context_length_exceeded( + exception: Exception, + messages: list[LLMMessage], + llm: LLM | BaseLLM, +) -> bool: + """Check if the response is null/empty because context length excedded. + + Args: + exception: The exception to check + + Returns: + bool: True if the exception is due to context length exceeding + """ + messages_string = " ".join([message["content"] for message in messages]) + cut_size = llm.get_context_window_size() + + messages_groups = [ + {"content": messages_string[i : i + cut_size]} + for i in range(0, len(messages_string), cut_size) + ] + return len(messages_groups) and isinstance(exception, ValueError) and "None or empty" in str(exception) + + def handle_context_length( respect_context_window: bool, printer: Printer, From cbd7b22b8e5d1cc1690f6da2d696580a186844ed Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Fri, 26 Sep 2025 19:52:07 +0530 Subject: [PATCH 03/18] Refractor the is_context_length_exceeded function --- src/crewai/utilities/agent_utils.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/crewai/utilities/agent_utils.py b/src/crewai/utilities/agent_utils.py index f2e0c5208c..893f606742 100644 --- a/src/crewai/utilities/agent_utils.py +++ b/src/crewai/utilities/agent_utils.py @@ -383,18 +383,31 @@ def handle_output_parser_exception( return formatted_answer -def is_context_length_exceeded(exception: Exception) -> bool: - """Check if the exception is due to context length exceeding. +def is_context_length_exceeded( + exception: Exception, + messages: list[LLMMessage], + llm: LLM | BaseLLM, +) -> bool: + """ + Check if the exception is due to context length exceeding or + response is empty because context length exceeded. Args: exception: The exception to check + messages: Messages sent to the LLM + llm: The LLM instance Returns: - bool: True if the exception is due to context length exceeding + True if the exception is due to context length exceeding or + the response is empty because of context length. """ - return LLMContextLengthExceededError(str(exception))._is_context_limit_error( + exceeded_error = LLMContextLengthExceededError(str(exception))._is_context_limit_error( str(exception) ) + null_response = is_null_response_because_context_length_exceeded( + exception=exception, messages=messages, llm=llm + ) + return exceeded_error or null_response def is_null_response_because_context_length_exceeded( From 0c52b0a7d88046f581a382b261e4e63e253fee0f Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Fri, 26 Sep 2025 19:53:40 +0530 Subject: [PATCH 04/18] Removed unnessary import --- src/crewai/agents/crew_agent_executor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/crewai/agents/crew_agent_executor.py b/src/crewai/agents/crew_agent_executor.py index b15061de10..d912bdf3c1 100644 --- a/src/crewai/agents/crew_agent_executor.py +++ b/src/crewai/agents/crew_agent_executor.py @@ -35,7 +35,6 @@ handle_unknown_error, has_reached_max_iterations, is_context_length_exceeded, - is_null_response_because_context_length_exceeded, process_llm_response, ) from crewai.utilities.constants import TRAINING_DATA_FILE From 73791561f1a12b1755162114357e8c5749a7c221 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Fri, 26 Sep 2025 19:57:19 +0530 Subject: [PATCH 05/18] Ruff checks --- src/crewai/utilities/agent_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crewai/utilities/agent_utils.py b/src/crewai/utilities/agent_utils.py index 893f606742..c4aff5b3b1 100644 --- a/src/crewai/utilities/agent_utils.py +++ b/src/crewai/utilities/agent_utils.py @@ -430,7 +430,7 @@ def is_null_response_because_context_length_exceeded( {"content": messages_string[i : i + cut_size]} for i in range(0, len(messages_string), cut_size) ] - return len(messages_groups) and isinstance(exception, ValueError) and "None or empty" in str(exception) + return len(messages_groups) and isinstance(exception, ValueError) and "None or empty" in str(exception) def handle_context_length( From 703b1639e0f29306e534e31fc4e03e3ad1fa073d Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Fri, 26 Sep 2025 23:39:08 +0530 Subject: [PATCH 06/18] Added test cases --- src/crewai/utilities/agent_utils.py | 2 +- tests/utilities/test_agent_utils.py | 70 +++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100755 tests/utilities/test_agent_utils.py diff --git a/src/crewai/utilities/agent_utils.py b/src/crewai/utilities/agent_utils.py index c4aff5b3b1..31b54c4975 100644 --- a/src/crewai/utilities/agent_utils.py +++ b/src/crewai/utilities/agent_utils.py @@ -430,7 +430,7 @@ def is_null_response_because_context_length_exceeded( {"content": messages_string[i : i + cut_size]} for i in range(0, len(messages_string), cut_size) ] - return len(messages_groups) and isinstance(exception, ValueError) and "None or empty" in str(exception) + return ((len(messages_groups) > 0) and isinstance(exception, ValueError) and "None or empty" in str(exception)) def handle_context_length( diff --git a/tests/utilities/test_agent_utils.py b/tests/utilities/test_agent_utils.py new file mode 100755 index 0000000000..865c50f103 --- /dev/null +++ b/tests/utilities/test_agent_utils.py @@ -0,0 +1,70 @@ +import pytest +from unittest.mock import MagicMock +from crewai.utilities.agent_utils import is_null_response_because_context_length_exceeded + +def test_is_null_response_because_context_length_exceeded_true(): + """ + Test that the function returns True when the exception is a ValueError + with 'None or empty' and there are messages. + """ + # Arrange + mock_llm = MagicMock() + mock_llm.get_context_window_size.return_value = 10 + exception = ValueError("Invalid response from LLM call - None or empty.") + messages = [{"content": "This is a test message."}] + + # Act + result = is_null_response_because_context_length_exceeded(exception, messages, mock_llm) + + # Assert + assert result is True + +def test_is_null_response_because_context_length_exceeded_false_wrong_exception(): + """ + Test that the function returns False when the exception is not a ValueError. + """ + # Arrange + mock_llm = MagicMock() + mock_llm.get_context_window_size.return_value = 10 + exception = TypeError("Some other error.") + messages = [{"content": "This is a test message."}] + + # Act + result = is_null_response_because_context_length_exceeded(exception, messages, mock_llm) + + # Assert + assert result is False + +def test_is_null_response_because_context_length_exceeded_false_wrong_message(): + """ + Test that the function returns False when the exception message does not + contain 'None or empty'. + """ + # Arrange + mock_llm = MagicMock() + mock_llm.get_context_window_size.return_value = 10 + exception = ValueError("Another value error.") + messages = [{"content": "This is a test message."}] + + # Act + result = is_null_response_because_context_length_exceeded(exception, messages, mock_llm) + + # Assert + assert result is False + +def test_is_null_response_because_context_length_exceeded_false_empty_messages(): + """ + Test that the function returns False when the messages list is empty. + """ + # Arrange + mock_llm = MagicMock() + mock_llm.get_context_window_size.return_value = 10 + exception = ValueError("Invalid response from LLM call - None or empty.") + messages = [] + + # Act + result = is_null_response_because_context_length_exceeded(exception, messages, mock_llm) + print(result) + + # Assert + assert result is False \ No newline at end of file From 07bb2aaf5d9f5423945cb23a3cd88d2d83f751ad Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Sat, 27 Sep 2025 01:53:01 +0530 Subject: [PATCH 07/18] Test case for the agent_executor --- src/crewai/agents/crew_agent_executor.py | 2 +- src/crewai/utilities/agent_utils.py | 1 + tests/agents/test_agent.py | 49 ++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/crewai/agents/crew_agent_executor.py b/src/crewai/agents/crew_agent_executor.py index d912bdf3c1..afb77c09c6 100644 --- a/src/crewai/agents/crew_agent_executor.py +++ b/src/crewai/agents/crew_agent_executor.py @@ -241,7 +241,7 @@ def _invoke_loop(self) -> AgentFinish: if e.__class__.__module__.startswith("litellm"): # Do not retry on litellm errors raise e - if is_context_length_exceeded(e): + if is_context_length_exceeded(exception=e, messages=self.messages, llm=self.llm): handle_context_length( respect_context_window=self.respect_context_window, printer=self._printer, diff --git a/src/crewai/utilities/agent_utils.py b/src/crewai/utilities/agent_utils.py index 31b54c4975..2ff3eed152 100644 --- a/src/crewai/utilities/agent_utils.py +++ b/src/crewai/utilities/agent_utils.py @@ -415,6 +415,7 @@ def is_null_response_because_context_length_exceeded( messages: list[LLMMessage], llm: LLM | BaseLLM, ) -> bool: + print("Insdie the function") """Check if the response is null/empty because context length excedded. Args: diff --git a/tests/agents/test_agent.py b/tests/agents/test_agent.py index e7d0526c80..b412c2c28f 100644 --- a/tests/agents/test_agent.py +++ b/tests/agents/test_agent.py @@ -1541,6 +1541,55 @@ def test_handle_context_length_exceeds_limit(): mock_summarize.assert_called_once() +def test_handle_context_length_exceeds_no_response(): + mock_agent_finish = MagicMock(spec=AgentFinish) + mock_agent_finish.output = "This is the final answer" + + llm = LLM(model="gpt-4o-mini",context_window_size = 2) + llm.context_window_size = 2 # Manually overriding it to be 2 + + exception_to_be_raised = ValueError("Invalid response from LLM call - None or empty.") + + with patch("crewai.agents.crew_agent_executor.handle_max_iterations_exceeded", return_value = mock_agent_finish) as mock_handle_max_iterations_exceeded: + with patch("crewai.agents.crew_agent_executor.get_llm_response") as mock_get_llm_response: + with patch("crewai.utilities.agent_utils.is_null_response_because_context_length_exceeded",return_value = True) as mock_is_null_response_because_context_length_exceeded: + with patch("crewai.utilities.agent_utils.summarize_messages") as mock_summarize_messages: + mock_get_llm_response.side_effect = exception_to_be_raised + + agent = Agent( + role="test role", + goal="test goal", + backstory="test backstory", + respect_context_window=True, + llm=llm, + max_iter = 0 + ) + + task = Task(description="The final answer is 42. But don't give it yet, instead keep using the `get_final_answer` tool.",expected_output="The final answer") + result = agent.execute_task( + task=task, + ) + + mock_get_llm_response.assert_called_once() + mock_is_null_response_because_context_length_exceeded.assert_called_once_with( + exception = exception_to_be_raised, + messages=[{'role': 'system', 'content': 'You are test role. test backstory\nYour personal goal is: test goal\nTo give my best complete final answer to the task respond using the exact following format:\n\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\nI MUST use these formats, my job depends on it!'}, {'role': 'user', 'content': "\nCurrent Task: The final answer is 42. But don't give it yet, instead keep using the `get_final_answer` tool.\n\nThis is the expected 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:"}], + llm = llm + ) + + mock_summarize_messages.assert_called_once() + + assert mock_is_null_response_because_context_length_exceeded( + exception = exception_to_be_raised, + messages=[{'role': 'system', 'content': 'You are test role. test backstory\nYour personal goal is: test goal\nTo give my best complete final answer to the task respond using the exact following format:\n\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\nI MUST use these formats, my job depends on it!'}, {'role': 'user', 'content': "\nCurrent Task: The final answer is 42. But don't give it yet, instead keep using the `get_final_answer` tool.\n\nThis is the expected 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:"}], + llm = llm + ) is True + + mock_summarize_messages.assert_called_once() + mock_handle_max_iterations_exceeded.assert_called_once() + assert result == "This is the final answer" + + @pytest.mark.vcr(filter_headers=["authorization"]) def test_handle_context_length_exceeds_limit_cli_no(): agent = Agent( From a0aae6fb0b769658d08b4a4077256e91f7621d50 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Sat, 27 Sep 2025 02:01:06 +0530 Subject: [PATCH 08/18] Mypy fixed --- src/crewai/agents/crew_agent_executor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/crewai/agents/crew_agent_executor.py b/src/crewai/agents/crew_agent_executor.py index afb77c09c6..377db348b3 100644 --- a/src/crewai/agents/crew_agent_executor.py +++ b/src/crewai/agents/crew_agent_executor.py @@ -40,6 +40,7 @@ from crewai.utilities.constants import TRAINING_DATA_FILE from crewai.utilities.tool_utils import execute_tool_and_check_finality from crewai.utilities.training_handler import CrewTrainingHandler +from crewai.utilities.types import LLMMessage class CrewAgentExecutor(CrewAgentExecutorMixin): @@ -111,7 +112,7 @@ def __init__( self.respect_context_window = respect_context_window self.request_within_rpm_limit = request_within_rpm_limit self.ask_for_human_input = False - self.messages: list[dict[str, str]] = [] + self.messages: list[LLMMessage] = [] self.iterations = 0 self.log_error_after = 3 existing_stop = self.llm.stop or [] From 29123db19ae15f4f2b4dcf59ebde6bc451795bcd Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Sat, 27 Sep 2025 02:10:12 +0530 Subject: [PATCH 09/18] more Mypy errors --- src/crewai/agents/agent_builder/base_agent_executor_mixin.py | 3 ++- src/crewai/utilities/agent_utils.py | 2 +- src/crewai/utilities/tool_utils.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/crewai/agents/agent_builder/base_agent_executor_mixin.py b/src/crewai/agents/agent_builder/base_agent_executor_mixin.py index 60de79dcc2..d2df54cfb0 100644 --- a/src/crewai/agents/agent_builder/base_agent_executor_mixin.py +++ b/src/crewai/agents/agent_builder/base_agent_executor_mixin.py @@ -8,6 +8,7 @@ from crewai.utilities.converter import ConverterError from crewai.utilities.evaluators.task_evaluator import TaskEvaluator from crewai.utilities.printer import Printer +from crewai.utilities.types import LLMMessage if TYPE_CHECKING: from crewai.agents.agent_builder.base_agent import BaseAgent @@ -21,7 +22,7 @@ class CrewAgentExecutorMixin: task: "Task" iterations: int max_iter: int - messages: list[dict[str, str]] + messages: list[LLMMessage] _i18n: I18N _printer: Printer = Printer() diff --git a/src/crewai/utilities/agent_utils.py b/src/crewai/utilities/agent_utils.py index 2ff3eed152..ba324a7070 100644 --- a/src/crewai/utilities/agent_utils.py +++ b/src/crewai/utilities/agent_utils.py @@ -286,7 +286,7 @@ def process_llm_response( def handle_agent_action_core( formatted_answer: AgentAction, tool_result: ToolResult, - messages: list[dict[str, str]] | None = None, + messages: list[LLMMessage] | None = None, step_callback: Callable | None = None, show_logs: Callable | None = None, ) -> AgentAction | AgentFinish: diff --git a/src/crewai/utilities/tool_utils.py b/src/crewai/utilities/tool_utils.py index 851492a049..e48cd65a78 100644 --- a/src/crewai/utilities/tool_utils.py +++ b/src/crewai/utilities/tool_utils.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING +from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.agents.parser import AgentAction from crewai.agents.tools_handler import ToolsHandler from crewai.security.fingerprint import Fingerprint @@ -11,7 +12,6 @@ from crewai.utilities.i18n import I18N if TYPE_CHECKING: - from crewai.agent import Agent from crewai.llm import LLM from crewai.llms.base_llm import BaseLLM from crewai.task import Task @@ -25,7 +25,7 @@ def execute_tool_and_check_finality( agent_role: str | None = None, tools_handler: ToolsHandler | None = None, task: Task | None = None, - agent: Agent | None = None, + agent: BaseAgent | None = None, function_calling_llm: BaseLLM | LLM | None = None, fingerprint_context: dict[str, str] | None = None, ) -> ToolResult: From 9742d6dba79c1d0baa8b35b9959dd0af11b7c6a4 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Sat, 27 Sep 2025 02:22:11 +0530 Subject: [PATCH 10/18] more mypy fixed --- src/crewai/agents/crew_agent_executor.py | 4 ++-- src/crewai/utilities/evaluators/task_evaluator.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/crewai/agents/crew_agent_executor.py b/src/crewai/agents/crew_agent_executor.py index 377db348b3..4079f348da 100644 --- a/src/crewai/agents/crew_agent_executor.py +++ b/src/crewai/agents/crew_agent_executor.py @@ -5,7 +5,7 @@ """ from collections.abc import Callable -from typing import Any +from typing import Any, Literal from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.agents.agent_builder.base_agent_executor_mixin import CrewAgentExecutorMixin @@ -310,7 +310,7 @@ def _invoke_step_callback( if self.step_callback: self.step_callback(formatted_answer) - def _append_message(self, text: str, role: str = "assistant") -> None: + def _append_message(self, text: str, role: Literal["user", "assistant", "system"] = "assistant") -> None: """Add message to conversation history. Args: diff --git a/src/crewai/utilities/evaluators/task_evaluator.py b/src/crewai/utilities/evaluators/task_evaluator.py index ad1b993cf3..e748bfb0de 100644 --- a/src/crewai/utilities/evaluators/task_evaluator.py +++ b/src/crewai/utilities/evaluators/task_evaluator.py @@ -4,6 +4,7 @@ from pydantic import BaseModel, Field +from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.events.event_bus import crewai_event_bus from crewai.events.types.task_events import TaskEvaluationEvent from crewai.llm import LLM @@ -12,7 +13,6 @@ from crewai.utilities.training_converter import TrainingConverter if TYPE_CHECKING: - from crewai.agent import Agent from crewai.task import Task @@ -55,7 +55,7 @@ class TaskEvaluator: original_agent: The agent to evaluate. """ - def __init__(self, original_agent: Agent) -> None: + def __init__(self, original_agent: BaseAgent) -> None: """Initializes the TaskEvaluator with the given LLM and agent. Args: From c66bdeaf6141a00bcd36147fdc7ac46e8da275b4 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Tue, 30 Sep 2025 08:57:56 +0530 Subject: [PATCH 11/18] fixed the logic --- src/crewai/utilities/agent_utils.py | 2 +- tests/utilities/test_agent_utils.py | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/crewai/utilities/agent_utils.py b/src/crewai/utilities/agent_utils.py index ba324a7070..f55d5e9dc1 100644 --- a/src/crewai/utilities/agent_utils.py +++ b/src/crewai/utilities/agent_utils.py @@ -431,7 +431,7 @@ def is_null_response_because_context_length_exceeded( {"content": messages_string[i : i + cut_size]} for i in range(0, len(messages_string), cut_size) ] - return ((len(messages_groups) > 0) and isinstance(exception, ValueError) and "None or empty" in str(exception)) + return ((len(messages_groups) > 1) and isinstance(exception, ValueError) and "None or empty" in str(exception)) def handle_context_length( diff --git a/tests/utilities/test_agent_utils.py b/tests/utilities/test_agent_utils.py index 865c50f103..81409e1e7c 100755 --- a/tests/utilities/test_agent_utils.py +++ b/tests/utilities/test_agent_utils.py @@ -19,6 +19,7 @@ def test_is_null_response_because_context_length_exceeded_true(): # Assert assert result is True + def test_is_null_response_because_context_length_exceeded_false_wrong_exception(): """ Test that the function returns False when the exception is not a ValueError. @@ -35,6 +36,7 @@ def test_is_null_response_because_context_length_exceeded_false_wrong_exception( # Assert assert result is False + def test_is_null_response_because_context_length_exceeded_false_wrong_message(): """ Test that the function returns False when the exception message does not @@ -52,6 +54,7 @@ def test_is_null_response_because_context_length_exceeded_false_wrong_message(): # Assert assert result is False + def test_is_null_response_because_context_length_exceeded_false_empty_messages(): """ Test that the function returns False when the messages list is empty. @@ -67,4 +70,24 @@ def test_is_null_response_because_context_length_exceeded_false_empty_messages() print(result) # Assert - assert result is False \ No newline at end of file + assert result is False + + +def test_is_null_response_because_context_length_exceeded_false(): + """ + Test that the function returns True when the exception is a ValueError + with 'None or empty' and there are messages. + """ + # Arrange + mock_llm = MagicMock() + mock_llm.get_context_window_size.return_value = 50 + exception = ValueError("Invalid response from LLM call - None or empty.") + messages = [{"content": "This is a test message."}] + + # Act + result = is_null_response_because_context_length_exceeded(exception, messages, mock_llm) + + # Assert + assert result is False + + From a3d16667c8a69ccdfc0e30eadff0250a582fa3d8 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Tue, 30 Sep 2025 09:22:41 +0530 Subject: [PATCH 12/18] removed debug statements --- src/crewai/utilities/agent_utils.py | 1 - tests/utilities/test_agent_utils.py | 1 - 2 files changed, 2 deletions(-) diff --git a/src/crewai/utilities/agent_utils.py b/src/crewai/utilities/agent_utils.py index f55d5e9dc1..0dabd52f68 100644 --- a/src/crewai/utilities/agent_utils.py +++ b/src/crewai/utilities/agent_utils.py @@ -415,7 +415,6 @@ def is_null_response_because_context_length_exceeded( messages: list[LLMMessage], llm: LLM | BaseLLM, ) -> bool: - print("Insdie the function") """Check if the response is null/empty because context length excedded. Args: diff --git a/tests/utilities/test_agent_utils.py b/tests/utilities/test_agent_utils.py index 81409e1e7c..47dda3a639 100755 --- a/tests/utilities/test_agent_utils.py +++ b/tests/utilities/test_agent_utils.py @@ -67,7 +67,6 @@ def test_is_null_response_because_context_length_exceeded_false_empty_messages() # Act result = is_null_response_because_context_length_exceeded(exception, messages, mock_llm) - print(result) # Assert assert result is False From 964b8e8865cbaa3cdb63ead73fdb105c44989cac Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Thu, 30 Oct 2025 18:35:54 +0530 Subject: [PATCH 13/18] fixing imports --- lib/crewai/tests/utilities/test_agent_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crewai/tests/utilities/test_agent_utils.py b/lib/crewai/tests/utilities/test_agent_utils.py index 47dda3a639..7e99b4d2fc 100755 --- a/lib/crewai/tests/utilities/test_agent_utils.py +++ b/lib/crewai/tests/utilities/test_agent_utils.py @@ -1,6 +1,6 @@ import pytest from unittest.mock import MagicMock -from crewai.utilities.agent_utils import is_null_response_because_context_length_exceeded +from src.crewai.utilities.agent_utils import is_null_response_because_context_length_exceeded def test_is_null_response_because_context_length_exceeded_true(): """ From d060128776215078eebba43e415bf105b624d04f Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Thu, 30 Oct 2025 18:48:43 +0530 Subject: [PATCH 14/18] Refractored a bit --- lib/crewai/src/crewai/agents/crew_agent_executor.py | 3 ++- lib/crewai/src/crewai/lite_agent.py | 3 ++- lib/crewai/src/crewai/utilities/agent_utils.py | 5 +---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/crewai/src/crewai/agents/crew_agent_executor.py b/lib/crewai/src/crewai/agents/crew_agent_executor.py index 296ead4725..ac7b699d0f 100644 --- a/lib/crewai/src/crewai/agents/crew_agent_executor.py +++ b/lib/crewai/src/crewai/agents/crew_agent_executor.py @@ -34,6 +34,7 @@ handle_unknown_error, has_reached_max_iterations, is_context_length_exceeded, + is_null_response_because_context_length_exceeded, process_llm_response, ) from crewai.utilities.constants import TRAINING_DATA_FILE @@ -271,7 +272,7 @@ def _invoke_loop(self) -> AgentFinish: if e.__class__.__module__.startswith("litellm"): # Do not retry on litellm errors raise e - if is_context_length_exceeded(exception=e, messages=self.messages, llm=self.llm): + if is_context_length_exceeded(exception=e) or is_null_response_because_context_length_exceeded(exception=e, messages=self.messages, llm=self.llm ): handle_context_length( respect_context_window=self.respect_context_window, printer=self._printer, diff --git a/lib/crewai/src/crewai/lite_agent.py b/lib/crewai/src/crewai/lite_agent.py index d613681e20..fecf0d2468 100644 --- a/lib/crewai/src/crewai/lite_agent.py +++ b/lib/crewai/src/crewai/lite_agent.py @@ -54,6 +54,7 @@ handle_unknown_error, has_reached_max_iterations, is_context_length_exceeded, + is_null_response_because_context_length_exceeded, parse_tools, process_llm_response, render_text_description_and_args, @@ -535,7 +536,7 @@ def _invoke_loop(self) -> AgentFinish: if e.__class__.__module__.startswith("litellm"): # Do not retry on litellm errors raise e - if is_context_length_exceeded(e): + if is_context_length_exceeded(exception=e) or is_null_response_because_context_length_exceeded(exception=e, messages=self.messages, llm=self.llm ): handle_context_length( respect_context_window=self.respect_context_window, printer=self._printer, diff --git a/lib/crewai/src/crewai/utilities/agent_utils.py b/lib/crewai/src/crewai/utilities/agent_utils.py index c89249a126..b3dce2300d 100644 --- a/lib/crewai/src/crewai/utilities/agent_utils.py +++ b/lib/crewai/src/crewai/utilities/agent_utils.py @@ -407,10 +407,7 @@ def is_context_length_exceeded( exceeded_error = LLMContextLengthExceededError(str(exception))._is_context_limit_error( str(exception) ) - null_response = is_null_response_because_context_length_exceeded( - exception=exception, messages=messages, llm=llm - ) - return exceeded_error or null_response + return exceeded_error def is_null_response_because_context_length_exceeded( From b6fa82619fb60d10ecb4968110b5f768ad76a3e8 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Thu, 30 Oct 2025 18:53:18 +0530 Subject: [PATCH 15/18] Linting Issue --- lib/crewai/src/crewai/utilities/agent_utils.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/crewai/src/crewai/utilities/agent_utils.py b/lib/crewai/src/crewai/utilities/agent_utils.py index b3dce2300d..becf7157d9 100644 --- a/lib/crewai/src/crewai/utilities/agent_utils.py +++ b/lib/crewai/src/crewai/utilities/agent_utils.py @@ -387,9 +387,7 @@ def handle_output_parser_exception( def is_context_length_exceeded( - exception: Exception, - messages: list[LLMMessage], - llm: LLM | BaseLLM, + exception: Exception ) -> bool: """ Check if the exception is due to context length exceeding or @@ -404,10 +402,9 @@ def is_context_length_exceeded( True if the exception is due to context length exceeding or the response is empty because of context length. """ - exceeded_error = LLMContextLengthExceededError(str(exception))._is_context_limit_error( + return LLMContextLengthExceededError(str(exception))._is_context_limit_error( str(exception) ) - return exceeded_error def is_null_response_because_context_length_exceeded( From b05773c721cd86d6556666668c04ac37b46e0313 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Thu, 30 Oct 2025 18:57:12 +0530 Subject: [PATCH 16/18] Removed this from liteagent --- lib/crewai/src/crewai/lite_agent.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/crewai/src/crewai/lite_agent.py b/lib/crewai/src/crewai/lite_agent.py index fecf0d2468..2ded4b981f 100644 --- a/lib/crewai/src/crewai/lite_agent.py +++ b/lib/crewai/src/crewai/lite_agent.py @@ -54,7 +54,6 @@ handle_unknown_error, has_reached_max_iterations, is_context_length_exceeded, - is_null_response_because_context_length_exceeded, parse_tools, process_llm_response, render_text_description_and_args, @@ -536,7 +535,7 @@ def _invoke_loop(self) -> AgentFinish: if e.__class__.__module__.startswith("litellm"): # Do not retry on litellm errors raise e - if is_context_length_exceeded(exception=e) or is_null_response_because_context_length_exceeded(exception=e, messages=self.messages, llm=self.llm ): + if is_context_length_exceeded(exception=e): handle_context_length( respect_context_window=self.respect_context_window, printer=self._printer, From 4d4856cc79be885ea5beb95ac52916764ff3c468 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Thu, 30 Oct 2025 20:20:29 +0530 Subject: [PATCH 17/18] Fixed the test case --- lib/crewai/tests/agents/test_agent.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/crewai/tests/agents/test_agent.py b/lib/crewai/tests/agents/test_agent.py index 1548913856..4580f36f64 100644 --- a/lib/crewai/tests/agents/test_agent.py +++ b/lib/crewai/tests/agents/test_agent.py @@ -1589,14 +1589,14 @@ def test_handle_context_length_exceeds_no_response(): mock_agent_finish = MagicMock(spec=AgentFinish) mock_agent_finish.output = "This is the final answer" - llm = LLM(model="gpt-4o-mini",context_window_size = 2) + llm = LLM(model="gpt-4o-mini",context_window_size = 2, api_key = "DUMMY") llm.context_window_size = 2 # Manually overriding it to be 2 exception_to_be_raised = ValueError("Invalid response from LLM call - None or empty.") with patch("crewai.agents.crew_agent_executor.handle_max_iterations_exceeded", return_value = mock_agent_finish) as mock_handle_max_iterations_exceeded: with patch("crewai.agents.crew_agent_executor.get_llm_response") as mock_get_llm_response: - with patch("crewai.utilities.agent_utils.is_null_response_because_context_length_exceeded",return_value = True) as mock_is_null_response_because_context_length_exceeded: + with patch("crewai.agents.crew_agent_executor.is_null_response_because_context_length_exceeded", return_value = True) as mock_is_null_response_because_context_length_exceeded: with patch("crewai.utilities.agent_utils.summarize_messages") as mock_summarize_messages: mock_get_llm_response.side_effect = exception_to_be_raised From 62d1a85102197b79f23b0cb7e8b1faae792dfe63 Mon Sep 17 00:00:00 2001 From: Vidit-Ostwal Date: Fri, 31 Oct 2025 19:50:23 +0530 Subject: [PATCH 18/18] Adding functionality to LiteAgent as well --- lib/crewai/src/crewai/lite_agent.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/crewai/src/crewai/lite_agent.py b/lib/crewai/src/crewai/lite_agent.py index 2ded4b981f..fecf0d2468 100644 --- a/lib/crewai/src/crewai/lite_agent.py +++ b/lib/crewai/src/crewai/lite_agent.py @@ -54,6 +54,7 @@ handle_unknown_error, has_reached_max_iterations, is_context_length_exceeded, + is_null_response_because_context_length_exceeded, parse_tools, process_llm_response, render_text_description_and_args, @@ -535,7 +536,7 @@ def _invoke_loop(self) -> AgentFinish: if e.__class__.__module__.startswith("litellm"): # Do not retry on litellm errors raise e - if is_context_length_exceeded(exception=e): + if is_context_length_exceeded(exception=e) or is_null_response_because_context_length_exceeded(exception=e, messages=self.messages, llm=self.llm ): handle_context_length( respect_context_window=self.respect_context_window, printer=self._printer,