From 88d2968fd5c5799bd76cda839e67a00d78e22f16 Mon Sep 17 00:00:00 2001 From: Lucas Gomide Date: Tue, 26 Aug 2025 18:24:58 -0300 Subject: [PATCH] chore: add deprecation notices to Task.max_retries (#3379) --- docs/en/concepts/tasks.mdx | 3 ++- docs/ko/concepts/tasks.mdx | 5 +++-- docs/pt-BR/concepts/tasks.mdx | 5 +++-- src/crewai/task.py | 26 ++++++++++++++++++++++---- tests/test_crew.py | 4 ++-- tests/test_task_guardrails.py | 8 ++++---- 6 files changed, 36 insertions(+), 15 deletions(-) diff --git a/docs/en/concepts/tasks.mdx b/docs/en/concepts/tasks.mdx index ff4389528..ac9073f13 100644 --- a/docs/en/concepts/tasks.mdx +++ b/docs/en/concepts/tasks.mdx @@ -59,6 +59,7 @@ crew = Crew( | **Output Pydantic** _(optional)_ | `output_pydantic` | `Optional[Type[BaseModel]]` | A Pydantic model for task output. | | **Callback** _(optional)_ | `callback` | `Optional[Any]` | Function/object to be executed after task completion. | | **Guardrail** _(optional)_ | `guardrail` | `Optional[Callable]` | Function to validate task output before proceeding to next task. | +| **Guardrail Max Retries** _(optional)_ | `guardrail_max_retries` | `Optional[int]` | Maximum number of retries when guardrail validation fails. Defaults to 3. | ## Creating Tasks @@ -452,7 +453,7 @@ task = Task( expected_output="A valid JSON object", agent=analyst, guardrail=validate_json_output, - max_retries=3 # Limit retry attempts + guardrail_max_retries=3 # Limit retry attempts ) ``` diff --git a/docs/ko/concepts/tasks.mdx b/docs/ko/concepts/tasks.mdx index 795087d2f..64120d64c 100644 --- a/docs/ko/concepts/tasks.mdx +++ b/docs/ko/concepts/tasks.mdx @@ -59,6 +59,7 @@ crew = Crew( | **Pydantic 출력** _(선택 사항)_ | `output_pydantic` | `Optional[Type[BaseModel]]` | 태스크 출력용 Pydantic 모델입니다. | | **콜백** _(선택 사항)_ | `callback` | `Optional[Any]` | 태스크 완료 후 실행할 함수/객체입니다. | | **가드레일** _(선택 사항)_ | `guardrail` | `Optional[Callable]` | 다음 태스크로 진행하기 전에 태스크 출력을 검증하는 함수입니다. | +| **가드레일 최대 재시도** _(선택 사항)_ | `guardrail_max_retries` | `Optional[int]` | 가드레일 검증 실패 시 최대 재시도 횟수입니다. 기본값은 3입니다. | ## 작업 생성하기 @@ -448,7 +449,7 @@ task = Task( expected_output="A valid JSON object", agent=analyst, guardrail=validate_json_output, - max_retries=3 # Limit retry attempts + guardrail_max_retries=3 # 재시도 횟수 제한 ) ``` @@ -899,4 +900,4 @@ except RuntimeError as e: 작업(task)은 CrewAI 에이전트의 행동을 이끄는 원동력입니다. 작업과 그 결과를 적절하게 정의함으로써, 에이전트가 독립적으로 또는 협업 단위로 효과적으로 작동할 수 있는 기반을 마련할 수 있습니다. 작업에 적합한 도구를 장착하고, 실행 과정을 이해하며, 견고한 검증 절차를 따르는 것은 CrewAI의 잠재력을 극대화하는 데 필수적입니다. -이를 통해 에이전트가 할당된 작업에 효과적으로 준비되고, 작업이 의도대로 수행될 수 있습니다. \ No newline at end of file +이를 통해 에이전트가 할당된 작업에 효과적으로 준비되고, 작업이 의도대로 수행될 수 있습니다. diff --git a/docs/pt-BR/concepts/tasks.mdx b/docs/pt-BR/concepts/tasks.mdx index 8489245da..41c6efda2 100644 --- a/docs/pt-BR/concepts/tasks.mdx +++ b/docs/pt-BR/concepts/tasks.mdx @@ -59,6 +59,7 @@ crew = Crew( | **Output Pydantic** _(opcional)_ | `output_pydantic` | `Optional[Type[BaseModel]]` | Um modelo Pydantic para a saída da tarefa. | | **Callback** _(opcional)_ | `callback` | `Optional[Any]` | Função/objeto a ser executado após a conclusão da tarefa. | | **Guardrail** _(opcional)_ | `guardrail` | `Optional[Callable]` | Função para validar a saída da tarefa antes de prosseguir para a próxima tarefa. | +| **Max Tentativas Guardrail** _(opcional)_ | `guardrail_max_retries` | `Optional[int]` | Número máximo de tentativas quando a validação do guardrail falha. Padrão é 3. | ## Criando Tarefas @@ -450,7 +451,7 @@ task = Task( expected_output="Um objeto JSON válido", agent=analyst, guardrail=validate_json_output, - max_retries=3 # Limite de tentativas + guardrail_max_retries=3 # Limite de tentativas ) ``` @@ -935,7 +936,7 @@ task = Task( description="Gerar dados", expected_output="Dados válidos", guardrail=validate_data, - max_retries=5 # Sobrescreve o limite padrão de tentativas + guardrail_max_retries=5 # Sobrescreve o limite padrão de tentativas ) ``` diff --git a/src/crewai/task.py b/src/crewai/task.py index 2e74d9466..5f504bb58 100644 --- a/src/crewai/task.py +++ b/src/crewai/task.py @@ -4,6 +4,7 @@ import json import logging import threading import uuid +import warnings from concurrent.futures import Future from copy import copy from hashlib import md5 @@ -157,8 +158,13 @@ class Task(BaseModel): default=None, description="Function or string description of a guardrail to validate task output before proceeding to next task", ) - max_retries: int = Field( - default=3, description="Maximum number of retries when guardrail fails" + max_retries: Optional[int] = Field( + default=None, + description="[DEPRECATED] Maximum number of retries when guardrail fails. Use guardrail_max_retries instead. Will be removed in v1.0.0" + ) + guardrail_max_retries: int = Field( + default=3, + description="Maximum number of retries when guardrail fails" ) retry_count: int = Field(default=0, description="Current number of retries") start_time: Optional[datetime.datetime] = Field( @@ -354,6 +360,18 @@ class Task(BaseModel): ) return self + @model_validator(mode="after") + def handle_max_retries_deprecation(self): + if self.max_retries is not None: + warnings.warn( + "The 'max_retries' parameter is deprecated and will be removed in CrewAI v1.0.0. " + "Please use 'guardrail_max_retries' instead.", + DeprecationWarning, + stacklevel=2 + ) + self.guardrail_max_retries = self.max_retries + return self + def execute_sync( self, agent: Optional[BaseAgent] = None, @@ -450,9 +468,9 @@ class Task(BaseModel): retry_count=self.retry_count, ) if not guardrail_result.success: - if self.retry_count >= self.max_retries: + if self.retry_count >= self.guardrail_max_retries: raise Exception( - f"Task failed guardrail validation after {self.max_retries} retries. " + f"Task failed guardrail validation after {self.guardrail_max_retries} retries. " f"Last error: {guardrail_result.error}" ) diff --git a/tests/test_crew.py b/tests/test_crew.py index df62d5e16..4c55ee4d5 100644 --- a/tests/test_crew.py +++ b/tests/test_crew.py @@ -4150,7 +4150,7 @@ def test_crew_with_failing_task_guardrails(): expected_output="A properly formatted report", agent=researcher, guardrail=strict_format_guardrail, - max_retries=3, + guardrail_max_retries=3, ) crew = Crew( @@ -4196,7 +4196,7 @@ def test_crew_guardrail_feedback_in_context(): expected_output="A response containing the keyword 'IMPORTANT'", agent=researcher, guardrail=format_guardrail, - max_retries=2, + guardrail_max_retries=2, ) crew = Crew(agents=[researcher], tasks=[task]) diff --git a/tests/test_task_guardrails.py b/tests/test_task_guardrails.py index 901b962b9..ffa80fccb 100644 --- a/tests/test_task_guardrails.py +++ b/tests/test_task_guardrails.py @@ -61,7 +61,7 @@ def test_task_with_failing_guardrail(): description="Test task", expected_output="Output", guardrail=guardrail, - max_retries=1, + guardrail_max_retries=1, ) # First execution fails guardrail, second succeeds @@ -88,7 +88,7 @@ def test_task_with_guardrail_retries(): description="Test task", expected_output="Output", guardrail=guardrail, - max_retries=2, + guardrail_max_retries=2, ) with pytest.raises(Exception) as exc_info: @@ -113,7 +113,7 @@ def test_guardrail_error_in_context(): description="Test task", expected_output="Output", guardrail=guardrail, - max_retries=1, + guardrail_max_retries=1, ) # Mock execute_task to succeed on second attempt @@ -265,7 +265,7 @@ def test_guardrail_when_an_error_occurs(sample_agent, task_output): agent=sample_agent, expected_output="A list of available books on the First World War", guardrail="Ensure the authors are from Italy", - max_retries=0, + guardrail_max_retries=0, ) task.execute_sync(agent=sample_agent)