diff --git a/src/crewai/agent.py b/src/crewai/agent.py index b5ba57a2a3..cfa7c8cae7 100644 --- a/src/crewai/agent.py +++ b/src/crewai/agent.py @@ -101,6 +101,20 @@ class Agent(BaseModel): @field_validator("id", mode="before") @classmethod def _deny_user_set_id(cls, v: Optional[UUID4]) -> None: + """ Deny the user from setting the ID. + + Args: + cls: The class instance. + v: The value to be checked. + + Returns: + None + + Raises: + PydanticCustomError: If the user tries to set the ID, a PydanticCustomError is raised with the message + "may_not_set_field" and details "{}". + """ + if v: raise PydanticCustomError( "may_not_set_field", "This field is not to be set by the user.", {} @@ -108,7 +122,14 @@ def _deny_user_set_id(cls, v: Optional[UUID4]) -> None: @model_validator(mode="after") def set_private_attrs(self): - """Set private attributes.""" + """ Set private attributes. + + Returns: + None + + Raises: + SomeException: An explanation of when this exception is raised. + """ self._logger = Logger(self.verbose) if self.max_rpm and not self._rpm_controller: self._rpm_controller = RPMController( @@ -118,7 +139,14 @@ def set_private_attrs(self): @model_validator(mode="after") def check_agent_executor(self) -> "Agent": - """Check if the agent executor is set.""" + """ Check if the agent executor is set. + + Returns: + The current instance of the class. + + Raises: + None + """ if not self.agent_executor: self.set_cache_handler(self.cache_handler) return self @@ -129,15 +157,15 @@ def execute_task( context: Optional[str] = None, tools: Optional[List[Any]] = None, ) -> str: - """Execute a task with the agent. + """ Execute a task with the agent. Args: - task: Task to execute. - context: Context to execute the task in. - tools: Tools to use for the task. + task (str): Task to execute. + context (Optional[str]?): Context to execute the task in. Defaults to None. + tools (Optional[List[Any]]?): Tools to use for the task. Defaults to None. Returns: - Output of the agent + str: Output of the agent """ if context: @@ -163,7 +191,7 @@ def execute_task( return result def set_cache_handler(self, cache_handler: CacheHandler) -> None: - """Set the cache handler for the agent. + """ Set the cache handler for the agent. Args: cache_handler: An instance of the CacheHandler class. @@ -173,17 +201,20 @@ def set_cache_handler(self, cache_handler: CacheHandler) -> None: self._create_agent_executor() def set_rpm_controller(self, rpm_controller: RPMController) -> None: - """Set the rpm controller for the agent. + """ Set the RPM controller for the agent. Args: - rpm_controller: An instance of the RPMController class. + rpm_controller (RPMController): An instance of the RPMController class. + + Returns: + None """ if not self._rpm_controller: self._rpm_controller = rpm_controller self._create_agent_executor() def _create_agent_executor(self) -> None: - """Create an agent executor for the agent. + """ Create an agent executor for the agent. Returns: An instance of the CrewAgentExecutor class. @@ -240,4 +271,13 @@ def _create_agent_executor(self) -> None: @staticmethod def __tools_names(tools) -> str: + """ Return a comma-separated string of tool names. + + Args: + tools (list): A list of tool objects. + + Returns: + str: A comma-separated string of tool names. + """ + return ", ".join([t.name for t in tools]) diff --git a/src/crewai/agents/cache/cache_handler.py b/src/crewai/agents/cache/cache_handler.py index 84c91c4cc9..c6fde524f3 100644 --- a/src/crewai/agents/cache/cache_handler.py +++ b/src/crewai/agents/cache/cache_handler.py @@ -7,12 +7,36 @@ class CacheHandler: _cache: dict = {} def __init__(self): + """ Initialize the object. + + Returns: + None + """ + self._cache = {} def add(self, tool, input, output): + """ Add the output of a tool with the given input to the cache. + + Args: + tool (str): The name of the tool. + input (str): The input for the tool. + output: The output generated by the tool. + """ + input = input.strip() self._cache[f"{tool}-{input}"] = output def read(self, tool, input) -> Optional[str]: + """ Read the cached data for the given tool and input. + + Args: + tool: The tool for which the data is being read. + input: The input for which the data is being read. + + Returns: + Optional[str]: The cached data for the given tool and input, or None if not found. + """ + input = input.strip() return self._cache.get(f"{tool}-{input}") diff --git a/src/crewai/agents/exceptions.py b/src/crewai/agents/exceptions.py index b2aea8efe7..57aaf22b92 100644 --- a/src/crewai/agents/exceptions.py +++ b/src/crewai/agents/exceptions.py @@ -11,6 +11,18 @@ class TaskRepeatedUsageException(OutputParserException): message: str def __init__(self, i18n: I18N, tool: str, tool_input: str, text: str): + """ Initialize the class with the provided parameters. + + Args: + i18n (I18N): An instance of the I18N class. + tool (str): The tool being used. + tool_input (str): The input for the tool. + text (str): The text associated with the task. + + Returns: + None + """ + self.i18n = i18n self.text = text self.tool = tool @@ -27,4 +39,10 @@ def __init__(self, i18n: I18N, tool: str, tool_input: str, text: str): ) def __str__(self): + """ Return the string representation of the object. + + Returns: + str: The string representation of the object. + """ + return self.message diff --git a/src/crewai/agents/executor.py b/src/crewai/agents/executor.py index bacf0b39d9..5a640b5288 100644 --- a/src/crewai/agents/executor.py +++ b/src/crewai/agents/executor.py @@ -25,13 +25,42 @@ class CrewAgentExecutor(AgentExecutor): @root_validator() def set_force_answer_max_iterations(cls, values: Dict) -> Dict: + """ Set the 'force_answer_max_iterations' value in the input dictionary. + + Args: + cls: The class instance (not used in the function) + values: A dictionary containing the input values with the key 'max_iterations' + + Returns: + A dictionary with the updated 'force_answer_max_iterations' value + + Raises: + KeyError: If 'max_iterations' key is not present in the input dictionary + TypeError: If the input 'values' is not a dictionary + """ + values["force_answer_max_iterations"] = values["max_iterations"] - 2 return values def _should_force_answer(self) -> bool: + """ Check if the answer should be forced based on the number of iterations. + + Returns: + bool: True if the number of iterations is equal to force_answer_max_iterations, False otherwise. + """ + return True if self.iterations == self.force_answer_max_iterations else False def _force_answer(self, output: AgentAction): + """ Force an answer with the given output. + + Args: + output (AgentAction): The action to be forced as the answer. + + Returns: + AgentStep: An AgentStep object with the forced action and an observation message. + """ + return AgentStep( action=output, observation=self.i18n.errors("force_final_answer") ) @@ -41,7 +70,15 @@ def _call( inputs: Dict[str, str], run_manager: Optional[CallbackManagerForChainRun] = None, ) -> Dict[str, Any]: - """Run text through and get agent response.""" + """ Run text through and get agent response. + + Args: + inputs (Dict[str, str]): The input data for the agent. + run_manager (Optional[CallbackManagerForChainRun]?): The callback manager for chain run. Defaults to None. + + Returns: + Dict[str, Any]: The agent response. + """ # Construct a mapping of tool name to tool for easy lookup name_to_tool_map = {tool.name: tool for tool in self.tools} # We construct a mapping from each tool to a color, used for logging. @@ -92,9 +129,27 @@ def _iter_next_step( intermediate_steps: List[Tuple[AgentAction, str]], run_manager: Optional[CallbackManagerForChainRun] = None, ) -> Iterator[Union[AgentFinish, AgentAction, AgentStep]]: - """Take a single step in the thought-action-observation loop. + """ Take a single step in the thought-action-observation loop. Override this to take control of how the agent makes and acts on choices. + + Args: + name_to_tool_map (Dict[str, BaseTool]): Mapping of tool names to BaseTool instances. + color_mapping (Dict[str, str]): Mapping of tool names to color codes. + inputs (Dict[str, str]): Input data for the agent. + intermediate_steps (List[Tuple[AgentAction, str]]): List of intermediate steps. + run_manager (Optional[CallbackManagerForChainRun]?): Callback manager for chain run. Defaults to None. + + Returns: + Iterator[Union[AgentFinish, AgentAction, AgentStep]]: Yields the next step in the agent's action loop. + + Yields: + Iterator[Union[AgentFinish, AgentAction, AgentStep]]: Yields the next step in the agent's action loop. + + Raises: + ValueError: If an unexpected output type is received from the agent. + ValueError: If an output parsing error occurs and `handle_parsing_errors` is not set to True. + ValueError: If an unexpected type of `handle_parsing_errors` is encountered. """ try: intermediate_steps = self._prepare_intermediate_steps(intermediate_steps) diff --git a/src/crewai/agents/output_parser.py b/src/crewai/agents/output_parser.py index 9edeb12b0d..e0fe31b589 100644 --- a/src/crewai/agents/output_parser.py +++ b/src/crewai/agents/output_parser.py @@ -50,6 +50,18 @@ class Config: i18n: I18N def parse(self, text: str) -> Union[AgentAction, AgentFinish, CacheHit]: + """ Parse the given text to extract action and input information. + + Args: + text (str): The input text to be parsed. + + Returns: + Union[AgentAction, AgentFinish, CacheHit]: An instance of AgentAction, AgentFinish, or CacheHit based on the parsed information. + + Raises: + TaskRepeatedUsageException: If the parsed action and input are the same as the last used tool. + """ + regex = ( r"Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)" ) diff --git a/src/crewai/agents/tools_handler.py b/src/crewai/agents/tools_handler.py index 24a81c4256..3aca41aa88 100644 --- a/src/crewai/agents/tools_handler.py +++ b/src/crewai/agents/tools_handler.py @@ -13,14 +13,31 @@ class ToolsHandler(BaseCallbackHandler): cache: CacheHandler def __init__(self, cache: CacheHandler, **kwargs: Any): - """Initialize the callback handler.""" + """ Initialize the callback handler. + + Args: + cache (CacheHandler): The cache object to be used. + **kwargs (Any): Additional keyword arguments. + + Returns: + None + """ self.cache = cache super().__init__(**kwargs) def on_tool_start( self, serialized: Dict[str, Any], input_str: str, **kwargs: Any ) -> Any: - """Run when tool starts running.""" + """ Run when tool starts running. + + Args: + serialized (Dict[str, Any]): Serialized data. + input_str (str): Input string. + **kwargs (Any): Additional keyword arguments. + + Returns: + Any: The return value is not specified. + """ name = serialized.get("name") if name not in ["invalid_tool", "_Exception"]: tools_usage = { @@ -30,7 +47,15 @@ def on_tool_start( self.last_used_tool = tools_usage def on_tool_end(self, output: str, **kwargs: Any) -> Any: - """Run when tool ends running.""" + """ Run when tool ends running. + + Args: + output (str): The output of the tool. + **kwargs (Any): Additional keyword arguments. + + Returns: + Any: The return value of the function. + """ if ( "is not a valid tool" not in output and "Invalid or incomplete response" not in output diff --git a/src/crewai/crew.py b/src/crewai/crew.py index 9ce34fc04d..5f837b5cb8 100644 --- a/src/crewai/crew.py +++ b/src/crewai/crew.py @@ -65,7 +65,15 @@ class Crew(BaseModel): @field_validator("id", mode="before") @classmethod def _deny_user_set_id(cls, v: Optional[UUID4]) -> None: - """Prevent manual setting of the 'id' field by users.""" + """ Prevent manual setting of the 'id' field by users. + + Args: + cls: The class instance. + v: The value to be set for the 'id' field. + + Raises: + PydanticCustomError: If the 'id' field is attempted to be set by the user. + """ if v: raise PydanticCustomError( "may_not_set_field", "The 'id' field cannot be set by the user.", {} @@ -76,11 +84,17 @@ def _deny_user_set_id(cls, v: Optional[UUID4]) -> None: def check_config_type( cls, v: Union[Json, Dict[str, Any]] ) -> Union[Json, Dict[str, Any]]: - """Validates that the config is a valid type. + """ Validates that the config is a valid type. + Args: + cls: The class instance. v: The config to be validated. + Returns: - The config if it is valid. + Union[Json, Dict[str, Any]]: The config if it is valid. + + Raises: + TypeError: If the config is not a valid type. """ # TODO: Improve typing @@ -88,7 +102,11 @@ def check_config_type( @model_validator(mode="after") def set_private_attrs(self) -> "Crew": - """Set private attributes.""" + """ Set private attributes. + + Returns: + Crew: Returns the instance of Crew with private attributes set. + """ self._cache_handler = CacheHandler() self._logger = Logger(self.verbose) self._rpm_controller = RPMController(max_rpm=self.max_rpm, logger=self._logger) @@ -96,7 +114,14 @@ def set_private_attrs(self) -> "Crew": @model_validator(mode="after") def check_manager_llm(self): - """Validates that the language model is set when using hierarchical process.""" + """ Validates that the language model is set when using hierarchical process. + + Returns: + self + + Raises: + PydanticCustomError: If `manager_llm` is missing when using hierarchical process. + """ if self.process == Process.hierarchical and not self.manager_llm: raise PydanticCustomError( "missing_manager_llm", @@ -107,7 +132,14 @@ def check_manager_llm(self): @model_validator(mode="after") def check_config(self): - """Validates that the crew is properly configured with agents and tasks.""" + """ Validates that the crew is properly configured with agents and tasks. + + Returns: + Crew: The crew object after validation. + + Raises: + PydanticCustomError: If the crew is not properly configured with agents and tasks. + """ if not self.config and not self.tasks and not self.agents: raise PydanticCustomError( "missing_keys", @@ -125,6 +157,13 @@ def check_config(self): return self def _setup_from_config(self): + """ Initializes agents and tasks from the provided config. + + Raises: + AssertionError: If self.config is None. + PydanticCustomError: If 'agents' or 'tasks' are missing in the config. + """ + assert self.config is not None, "Config should not be None." """Initializes agents and tasks from the provided config.""" @@ -137,13 +176,16 @@ def _setup_from_config(self): self.tasks = [self._create_task(task) for task in self.config["tasks"]] def _create_task(self, task_config: Dict[str, Any]) -> Task: - """Creates a task instance from its configuration. + """ Creates a task instance from its configuration. Args: - task_config: The configuration of the task. + task_config (Dict[str, Any]): The configuration of the task. Returns: - A task instance. + Task: A task instance. + + Raises: + KeyError: If the 'agent' key is not found in task_config. """ task_agent = next( agt for agt in self.agents if agt.role == task_config["agent"] @@ -152,7 +194,14 @@ def _create_task(self, task_config: Dict[str, Any]) -> Task: return Task(**task_config, agent=task_agent) def kickoff(self) -> str: - """Starts the crew to work on its assigned tasks.""" + """ Starts the crew to work on its assigned tasks. + + Returns: + str: The result of running the sequential or hierarchical process. + + Raises: + NotImplementedError: If the process is not implemented yet. + """ for agent in self.agents: agent.i18n = I18N(language=self.language) @@ -166,7 +215,14 @@ def kickoff(self) -> str: ) def _run_sequential_process(self) -> str: - """Executes tasks sequentially and returns the final output.""" + """ Executes tasks sequentially and returns the final output. + + Returns: + str: The final output after executing all tasks sequentially. + + Raises: + AnyException: If an error occurs during task execution. + """ task_output = "" for task in self.tasks: if task.agent is not None and task.agent.allow_delegation: @@ -192,7 +248,11 @@ def _run_sequential_process(self) -> str: return task_output def _run_hierarchical_process(self) -> str: - """Creates and assigns a manager agent to make sure the crew completes the tasks.""" + """ Creates and assigns a manager agent to make sure the crew completes the tasks. + + Returns: + str: The output of the hierarchical process. + """ i18n = I18N(language=self.language) manager = Agent( diff --git a/src/crewai/task.py b/src/crewai/task.py index 18d90aabf6..d4dff40ed1 100644 --- a/src/crewai/task.py +++ b/src/crewai/task.py @@ -54,6 +54,19 @@ class Config: @field_validator("id", mode="before") @classmethod def _deny_user_set_id(cls, v: Optional[UUID4]) -> None: + """ Deny user to set the ID. + + Args: + cls: The class instance. + v: The value to be checked. + + Returns: + None + + Raises: + PydanticCustomError: If the value is not None, indicating that the field should not be set by the user. + """ + if v: raise PydanticCustomError( "may_not_set_field", "This field is not to be set by the user.", {} @@ -61,7 +74,14 @@ def _deny_user_set_id(cls, v: Optional[UUID4]) -> None: @model_validator(mode="after") def check_tools(self): - """Check if the tools are set.""" + """ Check if the tools are set. + + Returns: + self + + Raises: + None + """ if not self.tools and self.agent and self.agent.tools: self.tools.extend(self.agent.tools) return self @@ -72,10 +92,18 @@ def execute( context: Optional[str] = None, tools: Optional[List[Any]] = None, ) -> str: - """Execute the task. + """ Execute the task. + + Args: + agent (Agent?): The agent to be used for execution. Defaults to None. + context (str?): The context for the task. Defaults to None. + tools (List[Any]?): The list of tools to be used. Defaults to None. Returns: - Output of the task. + str: Output of the task. + + Raises: + Exception: If the task has no agent assigned and cannot be executed directly. """ agent = agent or self.agent @@ -109,16 +137,28 @@ def execute( return result def _execute(self, agent, task_prompt, context, tools): + """ Execute a task using the provided agent and input parameters. + + Args: + agent (Agent): The agent to be used for executing the task. + task_prompt (str): The prompt for the task to be executed. + context (dict): The context for the task execution. + tools (list): The list of tools to be used for the task execution. + + Returns: + Any: The result of the task execution. + """ + result = agent.execute_task(task=task_prompt, context=context, tools=tools) self.output = TaskOutput(description=self.description, result=result) self.callback(self.output) if self.callback else None return result def _prompt(self) -> str: - """Prompt the task. + """ Prompt the task. Returns: - Prompt of the task. + str: Prompt of the task. """ tasks_slices = [self.description] diff --git a/src/crewai/tasks/task_output.py b/src/crewai/tasks/task_output.py index cfee529700..9970fc1156 100644 --- a/src/crewai/tasks/task_output.py +++ b/src/crewai/tasks/task_output.py @@ -12,6 +12,15 @@ class TaskOutput(BaseModel): @model_validator(mode="after") def set_summary(self): + """ Set a summary based on the first 10 words of the description. + + This method extracts the first 10 words from the description attribute, + concatenates them with an ellipsis, and assigns the result to the summary attribute. + + Returns: + self: The instance with the updated summary attribute. + """ + excerpt = " ".join(self.description.split(" ")[:10]) self.summary = f"{excerpt}..." return self diff --git a/src/crewai/tools/agent_tools.py b/src/crewai/tools/agent_tools.py index f3cb0b52cd..a9ad5ec915 100644 --- a/src/crewai/tools/agent_tools.py +++ b/src/crewai/tools/agent_tools.py @@ -14,6 +14,17 @@ class AgentTools(BaseModel): i18n: I18N = Field(default=I18N(), description="Internationalization settings.") def tools(self): + """ Return a list of tools available for the user. + + This method returns a list of Tool objects, each representing a specific tool available for the user to utilize. + + Returns: + list: A list of Tool objects. + + Raises: + None + """ + return [ Tool.from_function( func=self.delegate_work, @@ -32,15 +43,42 @@ def tools(self): ] def delegate_work(self, command): - """Useful to delegate a specific task to a coworker.""" + """ Useful to delegate a specific task to a coworker. + + Args: + self: The object instance + command: The specific task to be delegated + + Returns: + The result of executing the command + """ return self._execute(command) def ask_question(self, command): - """Useful to ask a question, opinion or take from a coworker.""" + """ Useful to ask a question, opinion or take from a coworker. + + Args: + command (str): The command to be executed. + + Returns: + The result of executing the command. + """ return self._execute(command) def _execute(self, command): - """Execute the command.""" + """ Execute the command. + + Args: + command (str): The command to be executed in the format "agent|task|context". + + Returns: + str: The result of executing the command. + + Raises: + ValueError: If the command does not contain all three parts separated by "|". + ValueError: If any of the parts (agent, task, context) is missing in the command. + ValueError: If the specified agent does not exist in the list of available agents. + """ try: agent, task, context = command.split("|") except ValueError: diff --git a/src/crewai/tools/cache_tools.py b/src/crewai/tools/cache_tools.py index a8e6dbf3c2..282bcbd1d2 100644 --- a/src/crewai/tools/cache_tools.py +++ b/src/crewai/tools/cache_tools.py @@ -15,6 +15,15 @@ class CacheTools(BaseModel): ) def tool(self): + """ Return a Tool object created from the hit_cache function. + + Args: + self: The current instance of the class. + + Returns: + Tool: A Tool object created from the hit_cache function. + """ + return Tool.from_function( func=self.hit_cache, name=self.name, @@ -22,6 +31,18 @@ def tool(self): ) def hit_cache(self, key): + """ Read data from the cache for the given key. + + Args: + key (str): The key used to retrieve data from the cache in the format "tool:|input:". + + Returns: + str: The data retrieved from the cache. + + Raises: + IndexError: If the key is not in the expected format. + """ + split = key.split("tool:") tool = split[1].split("|input:")[0].strip() tool_input = split[1].split("|input:")[1].strip() diff --git a/src/crewai/utilities/i18n.py b/src/crewai/utilities/i18n.py index 6dfcd688f2..249a4109de 100644 --- a/src/crewai/utilities/i18n.py +++ b/src/crewai/utilities/i18n.py @@ -14,7 +14,15 @@ class I18N(BaseModel): @model_validator(mode="after") def load_translation(self) -> "I18N": - """Load translations from a JSON file based on the specified language.""" + """ Load translations from a JSON file based on the specified language. + + Returns: + I18N: The instance of the class with loaded translations + + Raises: + ValidationError: If the translation file for the specified language is not found + ValidationError: If there is an error decoding JSON from the prompts file + """ try: dir_path = os.path.dirname(os.path.realpath(__file__)) prompts_path = os.path.join( @@ -36,15 +44,55 @@ def load_translation(self) -> "I18N": return self def slice(self, slice: str) -> str: + """ Retrieve the specified slice from the 'slices' collection. + + Args: + slice (str): The name of the slice to retrieve. + + Returns: + str: The retrieved slice. + """ + return self.retrieve("slices", slice) def errors(self, error: str) -> str: + """ Return the error message associated with the given error code. + + Args: + error (str): The error code for which the error message needs to be retrieved. + + Returns: + str: The error message associated with the given error code. + """ + return self.retrieve("errors", error) def tools(self, error: str) -> str: + """ Retrieve tools information. + + Args: + error (str): The error message. + + Returns: + str: The retrieved tools information. + """ + return self.retrieve("tools", error) def retrieve(self, kind, key) -> str: + """ Retrieve the translation for the given kind and key. + + Args: + kind (str): The kind of translation to retrieve. + key (str): The key of the translation to retrieve. + + Returns: + str: The translation corresponding to the given kind and key. + + Raises: + ValidationError: If the translation for the given kind and key is not found. + """ + try: return self._translations[kind][key] except: diff --git a/src/crewai/utilities/logger.py b/src/crewai/utilities/logger.py index e24d64250a..f5e3b1cc2e 100644 --- a/src/crewai/utilities/logger.py +++ b/src/crewai/utilities/logger.py @@ -1,11 +1,34 @@ class Logger: def __init__(self, verbose_level=0): + """ Initialize the class with a specified verbose level. + + Args: + verbose_level (int?): The level of verbosity. Defaults to 0. + + Returns: + None + """ + verbose_level = ( 2 if isinstance(verbose_level, bool) and verbose_level else verbose_level ) self.verbose_level = verbose_level def log(self, level, message): + """ Log a message at the specified level. + + Args: + level (str): The log level, should be one of "debug" or "info". + message (str): The message to be logged. + + Raises: + ValueError: If the specified log level is not "debug" or "info". + + Examples: + To log a message at the "info" level: + log("info", "This is an informational message.") + """ + level_map = {"debug": 1, "info": 2} if self.verbose_level and level_map.get(level, 0) <= self.verbose_level: print(f"[{level.upper()}]: {message}") diff --git a/src/crewai/utilities/prompts.py b/src/crewai/utilities/prompts.py index 4647543316..9d82c62ef1 100644 --- a/src/crewai/utilities/prompts.py +++ b/src/crewai/utilities/prompts.py @@ -14,19 +14,38 @@ class Prompts(BaseModel): SCRATCHPAD_SLICE: ClassVar[str] = "\n{agent_scratchpad}" def task_execution_with_memory(self) -> BasePromptTemplate: - """Generate a prompt for task execution with memory components.""" + """ Generate a prompt for task execution with memory components. + + Returns: + BasePromptTemplate: A prompt template for task execution with memory components. + """ return self._build_prompt(["role_playing", "tools", "memory", "task"]) def task_execution_without_tools(self) -> BasePromptTemplate: - """Generate a prompt for task execution without tools components.""" + """ Generate a prompt for task execution without tools components. + + Returns: + BasePromptTemplate: The generated prompt template. + """ return self._build_prompt(["role_playing", "task"]) def task_execution(self) -> BasePromptTemplate: - """Generate a standard prompt for task execution.""" + """ Generate a standard prompt for task execution. + + Returns: + BasePromptTemplate: A prompt template for task execution. + """ return self._build_prompt(["role_playing", "tools", "task"]) def _build_prompt(self, components: list[str]) -> BasePromptTemplate: - """Constructs a prompt string from specified components.""" + """ Constructs a prompt string from specified components. + + Args: + components (list[str]): List of components to construct the prompt string. + + Returns: + BasePromptTemplate: Prompt template constructed from the specified components. + """ prompt_parts = [self.i18n.slice(component) for component in components] prompt_parts.append(self.SCRATCHPAD_SLICE) return PromptTemplate.from_template("".join(prompt_parts)) diff --git a/src/crewai/utilities/rpm_controller.py b/src/crewai/utilities/rpm_controller.py index 84d4b641f5..99d8cf99a9 100644 --- a/src/crewai/utilities/rpm_controller.py +++ b/src/crewai/utilities/rpm_controller.py @@ -17,12 +17,31 @@ class RPMController(BaseModel): @model_validator(mode="after") def reset_counter(self): + """ Reset the counter and acquire a lock if max_rpm is set. + + Returns: + self + + Raises: + Any exceptions that may occur during the execution of this method. + """ + if self.max_rpm: self._lock = threading.Lock() self._reset_request_count() return self def check_or_wait(self): + """ Check if the current RPM is less than the maximum RPM, and if so, increment the current RPM by 1. + If the maximum RPM is reached, wait for the next minute to start and reset the current RPM to 1. + + Returns: + True if the current RPM is less than the maximum RPM or after waiting for the next minute and resetting the current RPM. + + Raises: + None + """ + if not self.max_rpm: return True @@ -39,15 +58,39 @@ def check_or_wait(self): return True def stop_rpm_counter(self): + """ Stop the RPM counter. + + This method cancels the timer if it is running and sets the timer attribute to None. + + Returns: + None + """ + if self._timer: self._timer.cancel() self._timer = None def _wait_for_next_minute(self): + """ Wait for the next minute and reset the current RPM to 0. + + This method uses time.sleep to pause the execution for 60 seconds and then resets the current RPM to 0. + + Raises: + Any exceptions raised by time.sleep. + """ + time.sleep(60) self._current_rpm = 0 def _reset_request_count(self): + """ Reset the request count and start a new timer for resetting the count after 60 seconds. + + This method resets the request count to 0 and starts a new timer to reset the count after 60 seconds. + + Raises: + : + """ + with self._lock: self._current_rpm = 0 if self._timer: