Repository: crewAIInc/crewAI
Pull request #958 (2024-07-17 18:43:18)

Reset memory


Patch:
diff --git a/docs/core-concepts/Crews.md b/docs/core-concepts/Crews.md
index b33f33d9..5464f0f5 100644
--- a/docs/core-concepts/Crews.md
+++ b/docs/core-concepts/Crews.md
@@ -32,6 +32,7 @@ A crew in crewAI represents a collaborative group of agents working together to
 | **Manager Agent** _(optional)_        | `manager_agent`        | `manager` sets a custom agent that will be used as a manager.                                                                                                                                                                                             |
 | **Manager Callbacks** _(optional)_    | `manager_callbacks`    | `manager_callbacks` takes a list of callback handlers to be executed by the manager agent when a hierarchical process is used.                                                                                                                            |
 | **Prompt File** _(optional)_          | `prompt_file`          | Path to the prompt JSON file to be used for the crew.                                                                                                                                                                                                     |
+| **Planning** *(optional)*             | `planning`             |  Adds planning ability to the Crew. When activated before each Crew iteration, all Crew data is sent to an AgentPlanner that will plan the tasks and this plan will be added to each task description.
 
 !!! note "Crew Max RPM"
 The `max_rpm` attribute sets the maximum number of requests per minute the crew can perform to avoid rate limits and will override individual agents' `max_rpm` settings if you set it.
@@ -215,7 +216,7 @@ You can now replay from a specific task using our cli command replay.
 
 The replay_from_tasks feature in CrewAI allows you to replay from a specific task using the command-line interface (CLI). By running the command `crewai replay -t <task_id>`, you can specify the `task_id` for the replay process.
 
-Kickoffs will now save the latest kickoffs returned task outputs locally for you to be able to replay from. 
+Kickoffs will now save the latest kickoffs returned task outputs locally for you to be able to replay from.
 
 
 ### Replaying from specific task Using the CLI
@@ -236,4 +237,4 @@ crewai log-tasks-outputs
 crewai replay -t <task_id>
 ```
 
-These commands let you replay from your latest kickoff tasks, still retaining context from previously executed tasks.
\ No newline at end of file
+These commands let you replay from your latest kickoff tasks, still retaining context from previously executed tasks.
diff --git a/docs/core-concepts/Memory.md b/docs/core-concepts/Memory.md
index 9c9d47be..0d1d3a67 100644
--- a/docs/core-concepts/Memory.md
+++ b/docs/core-concepts/Memory.md
@@ -161,6 +161,39 @@ my_crew = Crew(
 )
 ```
 
+### Resetting Memory
+```sh
+crewai reset_memories [OPTIONS]
+```
+
+#### Resetting Memory Options
+- **`-l, --long`**
+  - **Description:** Reset LONG TERM memory.
+  - **Type:** Flag (boolean)
+  - **Default:** False
+
+- **`-s, --short`**
+  - **Description:** Reset SHORT TERM memory.
+  - **Type:** Flag (boolean)
+  - **Default:** False
+
+- **`-e, --entities`**
+  - **Description:** Reset ENTITIES memory.
+  - **Type:** Flag (boolean)
+  - **Default:** False
+
+- **`-k, --kickoff-outputs`**
+  - **Description:** Reset LATEST KICKOFF TASK OUTPUTS.
+  - **Type:** Flag (boolean)
+  - **Default:** False
+
+- **`-a, --all`**
+  - **Description:** Reset ALL memories.
+  - **Type:** Flag (boolean)
+  - **Default:** False
+
+
+
 ## Benefits of Using crewAI's Memory System
 - **Adaptive Learning:** Crews become more efficient over time, adapting to new information and refining their approach to tasks.
 - **Enhanced Personalization:** Memory enables agents to remember user preferences and historical interactions, leading to personalized experiences.
diff --git a/docs/core-concepts/Planning.md b/docs/core-concepts/Planning.md
new file mode 100644
index 00000000..81030970
--- /dev/null
+++ b/docs/core-concepts/Planning.md
@@ -0,0 +1,119 @@
+---
+title: crewAI Planning
+description: Learn how to add planning to your crewAI Crew and improve their performance.
+---
+
+## Introduction
+The planning feature in CrewAI allows you to add planning capability to your crew. When enabled, before each Crew iteration, all Crew information is sent to an AgentPlanner that will plan the tasks step by step, and this plan will be added to each task description.
+
+### Using the Planning Feature
+Getting started with the planning feature is very easy, the only step required is to add `planning=True` to your Crew:
+
+```python
+from crewai import Crew, Agent, Task, Process
+
+# Assemble your crew with planning capabilities
+my_crew = Crew(
+    agents=self.agents,
+    tasks=self.tasks,
+    process=Process.sequential,
+    planning=True,
+)
+```
+
+From this point on, your crew will have planning enabled, and the tasks will be planned before each iteration.
+
+### Example
+
+When running the base case example, you will see something like the following output, which represents the output of the AgentPlanner responsible for creating the step-by-step logic to add to the Agents tasks.
+
+```bash
+
+[2024-07-15 16:49:11][INFO]: Planning the crew execution
+**Step-by-Step Plan for Task Execution**
+
+**Task Number 1: Conduct a thorough research about AI LLMs**
+
+**Agent:** AI LLMs Senior Data Researcher
+
+**Agent Goal:** Uncover cutting-edge developments in AI LLMs
+
+**Task Expected Output:** A list with 10 bullet points of the most relevant information about AI LLMs
+
+**Task Tools:** None specified
+
+**Agent Tools:** None specified
+
+**Step-by-Step Plan:**
+
+1. **Define Research Scope:**
+   - Determine the specific areas of AI LLMs to focus on, such as advancements in architecture, use cases, ethical considerations, and performance metrics.
+
+2. **Identify Reliable Sources:**
+   - List reputable sources for AI research, including academic journals, industry reports, conferences (e.g., NeurIPS, ACL), AI research labs (e.g., OpenAI, Google AI), and online databases (e.g., IEEE Xplore, arXiv).
+
+3. **Collect Data:**
+   - Search for the latest papers, articles, and reports published in 2023 and early 2024.
+   - Use keywords like "Large Language Models 2024", "AI LLM advancements", "AI ethics 2024", etc.
+
+4. **Analyze Findings:**
+   - Read and summarize the key points from each source.
+   - Highlight new techniques, models, and applications introduced in the past year.
+
+5. **Organize Information:**
+   - Categorize the information into relevant topics (e.g., new architectures, ethical implications, real-world applications).
+   - Ensure each bullet point is concise but informative.
+
+6. **Create the List:**
+   - Compile the 10 most relevant pieces of information into a bullet point list.
+   - Review the list to ensure clarity and relevance.
+
+**Expected Output:**
+A list with 10 bullet points of the most relevant information about AI LLMs.
+
+---
+
+**Task Number 2: Review the context you got and expand each topic into a full section for a report**
+
+**Agent:** AI LLMs Reporting Analyst
+
+**Agent Goal:** Create detailed reports based on AI LLMs data analysis and research findings
+
+**Task Expected Output:** A fully fledge report with the main topics, each with a full section of information. Formatted as markdown without '```'
+
+**Task Tools:** None specified
+
+**Agent Tools:** None specified
+
+**Step-by-Step Plan:**
+
+1. **Review the Bullet Points:**
+   - Carefully read through the list of 10 bullet points provided by the AI LLMs Senior Data Researcher.
+
+2. **Outline the Report:**
+   - Create an outline with each bullet point as a main section heading.
+   - Plan sub-sections under each main heading to cover different aspects of the topic.
+
+3. **Research Further Details:**
+   - For each bullet point, conduct additional research if necessary to gather more detailed information.
+   - Look for case studies, examples, and statistical data to support each section.
+
+4. **Write Detailed Sections:**
+   - Expand each bullet point into a comprehensive section.
+   - Ensure each section includes an introduction, detailed explanation, examples, and a conclusion.
+   - Use markdown formatting for headings, subheadings, lists, and emphasis.
+
+5. **Review and Edit:**
+   - Proofread the report for clarity, coherence, and correctness.
+   - Make sure the report flows logically from one section to the next.
+   - Format the report according to markdown standards.
+
+6. **Finalize the Report:**
+   - Ensure the report is complete with all sections expanded and detailed.
+   - Double-check formatting and make any necessary adjustments.
+
+**Expected Output:**
+A fully-fledged report with the main topics, each with a full section of information. Formatted as markdown without '```'.
+
+---
+```
diff --git a/docs/index.md b/docs/index.md
index b21c2068..77cdd985 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -43,6 +43,11 @@ Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By
                     Memory
                 </a>
             </li>
+            <li>
+                <a href="./core-concepts/Planning">
+                    Planning
+                </a>
+            </li>
         </ul>
     </div>
     <div style="width:30%">
diff --git a/mkdocs.yml b/mkdocs.yml
index 6e8cae1c..7caac90b 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -128,6 +128,7 @@ nav:
     - Collaboration: 'core-concepts/Collaboration.md'
     - Training: 'core-concepts/Training-Crew.md'
     - Memory: 'core-concepts/Memory.md'
+    - Planning: 'core-concepts/Planning.md'
     - Using LangChain Tools: 'core-concepts/Using-LangChain-Tools.md'
     - Using LlamaIndex Tools: 'core-concepts/Using-LlamaIndex-Tools.md'
   - How to Guides:
diff --git a/src/crewai/cli/cli.py b/src/crewai/cli/cli.py
index 6b803824..7b025a56 100644
--- a/src/crewai/cli/cli.py
+++ b/src/crewai/cli/cli.py
@@ -9,6 +9,7 @@ from crewai.memory.storage.kickoff_task_outputs_storage import (
 from .create_crew import create_crew
 from .train_crew import train_crew
 from .replay_from_task import replay_task_command
+from .reset_memories_command import reset_memories_command
 
 
 @click.group()
@@ -99,5 +100,31 @@ def log_tasks_outputs() -> None:
         click.echo(f"An error occurred while logging task outputs: {e}", err=True)
 
 
+@crewai.command()
+@click.option("-l", "--long", is_flag=True, help="Reset LONG TERM memory")
+@click.option("-s", "--short", is_flag=True, help="Reset SHORT TERM memory")
+@click.option("-e", "--entities", is_flag=True, help="Reset ENTITIES memory")
+@click.option(
+    "-k",
+    "--kickoff-outputs",
+    is_flag=True,
+    help="Reset LATEST KICKOFF TASK OUTPUTS",
+)
+@click.option("-a", "--all", is_flag=True, help="Reset ALL memories")
+def reset_memories(long, short, entities, kickoff_outputs, all):
+    """
+    Reset the crew memories (long, short, entity, latest_crew_kickoff_ouputs). This will delete all the data saved.
+    """
+    try:
+        if not all and not (long or short or entities or kickoff_outputs):
+            click.echo(
+                "Please specify at least one memory type to reset using the appropriate flags."
+            )
+            return
+        reset_memories_command(long, short, entities, kickoff_outputs, all)
+    except Exception as e:
+        click.echo(f"An error occurred while resetting memories: {e}", err=True)
+
+
 if __name__ == "__main__":
     crewai()
diff --git a/src/crewai/cli/reset_memories_command.py b/src/crewai/cli/reset_memories_command.py
new file mode 100644
index 00000000..68d82a92
--- /dev/null
+++ b/src/crewai/cli/reset_memories_command.py
@@ -0,0 +1,45 @@
+import subprocess
+import click
+
+from crewai.memory.entity.entity_memory import EntityMemory
+from crewai.memory.long_term.long_term_memory import LongTermMemory
+from crewai.memory.short_term.short_term_memory import ShortTermMemory
+from crewai.utilities.task_output_storage_handler import TaskOutputStorageHandler
+
+
+def reset_memories_command(long, short, entity, kickoff_outputs, all) -> None:
+    """
+    Replay the crew execution from a specific task.
+
+    Args:
+      task_id (str): The ID of the task to replay from.
+    """
+
+    try:
+        if all:
+            ShortTermMemory().reset()
+            EntityMemory().reset()
+            LongTermMemory().reset()
+            TaskOutputStorageHandler().reset()
+            click.echo("All memories have been reset.")
+        else:
+            if long:
+                LongTermMemory().reset()
+                click.echo("Long term memory has been reset.")
+
+            if short:
+                ShortTermMemory().reset()
+                click.echo("Short term memory has been reset.")
+            if entity:
+                EntityMemory().reset()
+                click.echo("Entity memory has been reset.")
+            if kickoff_outputs:
+                TaskOutputStorageHandler().reset()
+                click.echo("Latest Kickoff outputs stored has been reset.")
+
+    except subprocess.CalledProcessError as e:
+        click.echo(f"An error occurred while resetting the memories: {e}", err=True)
+        click.echo(e.output, err=True)
+
+    except Exception as e:
+        click.echo(f"An unexpected error occurred: {e}", err=True)
diff --git a/src/crewai/crew.py b/src/crewai/crew.py
index 29f0ea49..225083ab 100644
--- a/src/crewai/crew.py
+++ b/src/crewai/crew.py
@@ -42,6 +42,7 @@ from crewai.utilities.formatter import (
     aggregate_raw_outputs_from_task_outputs,
     aggregate_raw_outputs_from_tasks,
 )
+from crewai.utilities.planning_handler import CrewPlanner
 from crewai.utilities.task_output_storage_handler import TaskOutputStorageHandler
 from crewai.utilities.training_handler import CrewTrainingHandler
 
@@ -73,6 +74,7 @@ class Crew(BaseModel):
         task_callback: Callback to be executed after each task for every agents execution.
         step_callback: Callback to be executed after each step for every agents execution.
         share_crew: Whether you want to share the complete crew information and execution with crewAI to make the library better, and allow us to train models.
+        planning: Plan the crew execution and add the plan to the crew.
     """
 
     __hash__ = object.__hash__  # type: ignore
@@ -148,6 +150,10 @@ class Crew(BaseModel):
         default=False,
         description="output_log_file",
     )
+    planning: Optional[bool] = Field(
+        default=False,
+        description="Plan the crew execution and add the plan to the crew.",
+    )
     task_execution_output_json_files: Optional[List[str]] = Field(
         default=None,
         description="List of file paths for task execution JSON files.",
@@ -453,6 +459,9 @@ class Crew(BaseModel):
 
             agent.create_agent_executor()
 
+        if self.planning:
+            self._handle_crew_planning()
+
         metrics = []
 
         if self.process == Process.sequential:
@@ -547,6 +556,14 @@ class Crew(BaseModel):
         self._task_output_handler.reset()
         return results
 
+    def _handle_crew_planning(self):
+        """Handles the Crew planning."""
+        self._logger.log("info", "Planning the crew execution")
+        result = CrewPlanner(self.tasks)._handle_crew_planning()
+
+        for task, step_plan in zip(self.tasks, result.list_of_plans_per_task):
+            task.description += step_plan
+
     def _store_execution_log(
         self,
         task: Task,
diff --git a/src/crewai/memory/entity/entity_memory.py b/src/crewai/memory/entity/entity_memory.py
index 519d7a62..50aaeeaa 100644
--- a/src/crewai/memory/entity/entity_memory.py
+++ b/src/crewai/memory/entity/entity_memory.py
@@ -23,3 +23,9 @@ class EntityMemory(Memory):
         """Saves an entity item into the SQLite storage."""
         data = f"{item.name}({item.type}): {item.description}"
         super().save(data, item.metadata)
+
+    def reset(self) -> None:
+        try:
+            self.storage.reset()
+        except Exception as e:
+            raise Exception(f"An error occurred while resetting the entity memory: {e}")
diff --git a/src/crewai/memory/long_term/long_term_memory.py b/src/crewai/memory/long_term/long_term_memory.py
index b437d9e7..04126810 100644
--- a/src/crewai/memory/long_term/long_term_memory.py
+++ b/src/crewai/memory/long_term/long_term_memory.py
@@ -30,3 +30,6 @@ class LongTermMemory(Memory):
 
     def search(self, task: str, latest_n: int = 3) -> Dict[str, Any]:
         return self.storage.load(task, latest_n)  # type: ignore # BUG?: "Storage" has no attribute "load"
+
+    def reset(self) -> None:
+        self.storage.reset()
diff --git a/src/crewai/memory/short_term/short_term_memory.py b/src/crewai/memory/short_term/short_term_memory.py
index e9410ebb..ec65ad6f 100644
--- a/src/crewai/memory/short_term/short_term_memory.py
+++ b/src/crewai/memory/short_term/short_term_memory.py
@@ -18,8 +18,16 @@ class ShortTermMemory(Memory):
         )
         super().__init__(storage)
 
-    def save(self, item: ShortTermMemoryItem) -> None:  # type: ignore # BUG?: Signature of "save" incompatible with supertype "Memory"
+    def save(self, item: ShortTermMemoryItem) -> None:
         super().save(item.data, item.metadata, item.agent)
 
     def search(self, query: str, score_threshold: float = 0.35):
         return self.storage.search(query=query, score_threshold=score_threshold)  # type: ignore # BUG? The reference is to the parent class, but the parent class does not have this parameters
+
+    def reset(self) -> None:
+        try:
+            self.storage.reset()
+        except Exception as e:
+            raise Exception(
+                f"An error occurred while resetting the short-term memory: {e}"
+            )
diff --git a/src/crewai/memory/storage/interface.py b/src/crewai/memory/storage/interface.py
index 75c5a7b7..e988862b 100644
--- a/src/crewai/memory/storage/interface.py
+++ b/src/crewai/memory/storage/interface.py
@@ -9,3 +9,6 @@ class Storage:
 
     def search(self, key: str) -> Dict[str, Any]:  # type: ignore
         pass
+
+    def reset(self) -> None:
+        pass
diff --git a/src/crewai/memory/storage/ltm_sqlite_storage.py b/src/crewai/memory/storage/ltm_sqlite_storage.py
index 9825abad..7fb388a6 100644
--- a/src/crewai/memory/storage/ltm_sqlite_storage.py
+++ b/src/crewai/memory/storage/ltm_sqlite_storage.py
@@ -103,3 +103,20 @@ class LTMSQLiteStorage:
                 color="red",
             )
         return None
+
+    def reset(
+        self,
+    ) -> None:
+        """Resets the LTM table with error handling."""
+        try:
+            with sqlite3.connect(self.db_path) as conn:
+                cursor = conn.cursor()
+                cursor.execute("DELETE FROM long_term_memories")
+                conn.commit()
+
+        except sqlite3.Error as e:
+            self._printer.print(
+                content=f"MEMORY ERROR: An error occurred while deleting all rows in LTM: {e}",
+                color="red",
+            )
+        return None
diff --git a/src/crewai/memory/storage/rag_storage.py b/src/crewai/memory/storage/rag_storage.py
index 9a332ebf..e53f096e 100644
--- a/src/crewai/memory/storage/rag_storage.py
+++ b/src/crewai/memory/storage/rag_storage.py
@@ -2,6 +2,7 @@ import contextlib
 import io
 import logging
 import os
+import shutil
 from typing import Any, Dict, List, Optional
 
 from embedchain import App
@@ -71,13 +72,13 @@ class RAGStorage(Storage):
 
         if embedder_config:
             config["embedder"] = embedder_config
-
+        self.type = type
         self.app = App.from_config(config=config)
         self.app.llm = FakeLLM()
         if allow_reset:
             self.app.reset()
 
-    def save(self, value: Any, metadata: Dict[str, Any]) -> None:  # type: ignore # BUG?: Should be save(key, value, metadata)  Signature of "save" incompatible with supertype "Storage"
+    def save(self, value: Any, metadata: Dict[str, Any]) -> None:
         self._generate_embedding(value, metadata)
 
     def search(  # type: ignore # BUG?: Signature of "search" incompatible with supertype "Storage"
@@ -102,3 +103,11 @@ class RAGStorage(Storage):
     def _generate_embedding(self, text: str, metadata: Dict[str, Any]) -> Any:
         with suppress_logging():
             self.app.add(text, data_type="text", metadata=metadata)
+
+    def reset(self) -> None:
+        try:
+            shutil.rmtree(f"{db_storage_path()}/{self.type}")
+        except Exception as e:
+            raise Exception(
+                f"An error occurred while resetting the {self.type} memory: {e}"
+            )
diff --git a/src/crewai/task.py b/src/crewai/task.py
index 57a788f4..9e98adb1 100644
--- a/src/crewai/task.py
+++ b/src/crewai/task.py
@@ -254,7 +254,9 @@ class Task(BaseModel):
             content = (
                 json_output
                 if json_output
-                else pydantic_output.model_dump_json() if pydantic_output else result
+                else pydantic_output.model_dump_json()
+                if pydantic_output
+                else result
             )
             self._save_file(content)
 
@@ -326,9 +328,14 @@ class Task(BaseModel):
 
     def _create_converter(self, *args, **kwargs) -> Converter:
         """Create a converter instance."""
-        converter = self.agent.get_output_converter(*args, **kwargs)
-        if self.converter_cls:
+        if self.agent and not self.converter_cls:
+            converter = self.agent.get_output_converter(*args, **kwargs)
+        elif self.converter_cls:
             converter = self.converter_cls(*args, **kwargs)
+
+        if not converter:
+            raise Exception("No output converter found or set.")
+
         return converter
 
     def _export_output(
diff --git a/src/crewai/utilities/planning_handler.py b/src/crewai/utilities/planning_handler.py
new file mode 100644
index 00000000..2c5cd50a
--- /dev/null
+++ b/src/crewai/utilities/planning_handler.py
@@ -0,0 +1,64 @@
+from typing import List
+
+from pydantic import BaseModel
+
+from crewai.agent import Agent
+from crewai.task import Task
+
+
+class PlannerTaskPydanticOutput(BaseModel):
+    list_of_plans_per_task: List[str]
+
+
+class CrewPlanner:
+    def __init__(self, tasks: List[Task]):
+        self.tasks = tasks
+
+    def _handle_crew_planning(self):
+        """Handles the Crew planning by creating detailed step-by-step plans for each task."""
+        planning_agent = self._create_planning_agent()
+        tasks_summary = self._create_tasks_summary()
+
+        planner_task = self._create_planner_task(planning_agent, tasks_summary)
+
+        return planner_task.execute_sync()
+
+    def _create_planning_agent(self) -> Agent:
+        """Creates the planning agent for the crew planning."""
+        return Agent(
+            role="Task Execution Planner",
+            goal=(
+                "Your goal is to create an extremely detailed, step-by-step plan based on the tasks and tools "
+                "available to each agent so that they can perform the tasks in an exemplary manner"
+            ),
+            backstory="Planner agent for crew planning",
+        )
+
+    def _create_planner_task(self, planning_agent: Agent, tasks_summary: str) -> Task:
+        """Creates the planner task using the given agent and tasks summary."""
+        return Task(
+            description=(
+                f"Based on these tasks summary: {tasks_summary} \n Create the most descriptive plan based on the tasks "
+                "descriptions, tools available, and agents' goals for them to execute their goals with perfection."
+            ),
+            expected_output="Step by step plan on how the agents can execute their tasks using the available tools with mastery",
+            agent=planning_agent,
+            output_pydantic=PlannerTaskPydanticOutput,
+        )
+
+    def _create_tasks_summary(self) -> str:
+        """Creates a summary of all tasks."""
+        tasks_summary = []
+        for idx, task in enumerate(self.tasks):
+            tasks_summary.append(
+                f"""
+                Task Number {idx + 1} - {task.description}
+                "task_description": {task.description}
+                "task_expected_output": {task.expected_output}
+                "agent": {task.agent.role if task.agent else "None"}
+                "agent_goal": {task.agent.goal if task.agent else "None"}
+                "task_tools": {task.tools}
+                "agent_tools": {task.agent.tools if task.agent else "None"}
+                """
+            )
+        return " ".join(tasks_summary)


Test patch:
diff --git a/tests/cli/cli_test.py b/tests/cli/cli_test.py
index f2c6879b..f877f913 100644
--- a/tests/cli/cli_test.py
+++ b/tests/cli/cli_test.py
@@ -3,7 +3,7 @@ from unittest import mock
 import pytest
 from click.testing import CliRunner
 
-from crewai.cli.cli import train, version
+from crewai.cli.cli import train, version, reset_memories
 
 
 @pytest.fixture
@@ -41,6 +41,82 @@ def test_train_invalid_string_iterations(train_crew, runner):
     )
 
 
+@mock.patch("crewai.cli.reset_memories_command.ShortTermMemory")
+@mock.patch("crewai.cli.reset_memories_command.EntityMemory")
+@mock.patch("crewai.cli.reset_memories_command.LongTermMemory")
+@mock.patch("crewai.cli.reset_memories_command.TaskOutputStorageHandler")
+def test_reset_all_memories(
+    MockTaskOutputStorageHandler,
+    MockLongTermMemory,
+    MockEntityMemory,
+    MockShortTermMemory,
+    runner,
+):
+    result = runner.invoke(reset_memories, ["--all"])
+    MockShortTermMemory().reset.assert_called_once()
+    MockEntityMemory().reset.assert_called_once()
+    MockLongTermMemory().reset.assert_called_once()
+    MockTaskOutputStorageHandler().reset.assert_called_once()
+
+    assert result.output == "All memories have been reset.\n"
+
+
+@mock.patch("crewai.cli.reset_memories_command.ShortTermMemory")
+def test_reset_short_term_memories(MockShortTermMemory, runner):
+    result = runner.invoke(reset_memories, ["-s"])
+    MockShortTermMemory().reset.assert_called_once()
+    assert result.output == "Short term memory has been reset.\n"
+
+
+@mock.patch("crewai.cli.reset_memories_command.EntityMemory")
+def test_reset_entity_memories(MockEntityMemory, runner):
+    result = runner.invoke(reset_memories, ["-e"])
+    MockEntityMemory().reset.assert_called_once()
+    assert result.output == "Entity memory has been reset.\n"
+
+
+@mock.patch("crewai.cli.reset_memories_command.LongTermMemory")
+def test_reset_long_term_memories(MockLongTermMemory, runner):
+    result = runner.invoke(reset_memories, ["-l"])
+    MockLongTermMemory().reset.assert_called_once()
+    assert result.output == "Long term memory has been reset.\n"
+
+
+@mock.patch("crewai.cli.reset_memories_command.TaskOutputStorageHandler")
+def test_reset_kickoff_outputs(MockTaskOutputStorageHandler, runner):
+    result = runner.invoke(reset_memories, ["-k"])
+    MockTaskOutputStorageHandler().reset.assert_called_once()
+    assert result.output == "Latest Kickoff outputs stored has been reset.\n"
+
+
+@mock.patch("crewai.cli.reset_memories_command.ShortTermMemory")
+@mock.patch("crewai.cli.reset_memories_command.LongTermMemory")
+def test_reset_multiple_memory_flags(MockShortTermMemory, MockLongTermMemory, runner):
+    result = runner.invoke(
+        reset_memories,
+        [
+            "-s",
+            "-l",
+        ],
+    )
+    MockShortTermMemory().reset.assert_called_once()
+    MockLongTermMemory().reset.assert_called_once()
+    assert (
+        result.output
+        == "Long term memory has been reset.\nShort term memory has been reset.\n"
+    )
+
+
+def test_reset_no_memory_flags(runner):
+    result = runner.invoke(
+        reset_memories,
+    )
+    assert (
+        result.output
+        == "Please specify at least one memory type to reset using the appropriate flags.\n"
+    )
+
+
 def test_version_command(runner):
     result = runner.invoke(version)
 
diff --git a/tests/task_test.py b/tests/task_test.py
index 412e42dc..6458ea35 100644
--- a/tests/task_test.py
+++ b/tests/task_test.py
@@ -5,12 +5,13 @@ import json
 from unittest.mock import MagicMock, patch
 
 import pytest
+from pydantic import BaseModel
+from pydantic_core import ValidationError
+
 from crewai import Agent, Crew, Process, Task
 from crewai.tasks.conditional_task import ConditionalTask
 from crewai.tasks.task_output import TaskOutput
 from crewai.utilities.converter import Converter
-from pydantic import BaseModel
-from pydantic_core import ValidationError
 
 
 def test_task_tool_reflect_agent_tools():
diff --git a/tests/utilities/test_planning_handler.py b/tests/utilities/test_planning_handler.py
new file mode 100644
index 00000000..c76879d2
--- /dev/null
+++ b/tests/utilities/test_planning_handler.py
@@ -0,0 +1,68 @@
+from unittest.mock import patch
+
+import pytest
+
+from crewai.agent import Agent
+from crewai.task import Task
+from crewai.utilities.planning_handler import CrewPlanner, PlannerTaskPydanticOutput
+
+
+class TestCrewPlanner:
+    @pytest.fixture
+    def crew_planner(self):
+        tasks = [
+            Task(
+                description="Task 1",
+                expected_output="Output 1",
+                agent=Agent(role="Agent 1", goal="Goal 1", backstory="Backstory 1"),
+            ),
+            Task(
+                description="Task 2",
+                expected_output="Output 2",
+                agent=Agent(role="Agent 2", goal="Goal 2", backstory="Backstory 2"),
+            ),
+            Task(
+                description="Task 3",
+                expected_output="Output 3",
+                agent=Agent(role="Agent 3", goal="Goal 3", backstory="Backstory 3"),
+            ),
+        ]
+        return CrewPlanner(tasks)
+
+    def test_handle_crew_planning(self, crew_planner):
+        with patch.object(Task, "execute_sync") as execute:
+            execute.return_value = PlannerTaskPydanticOutput(
+                list_of_plans_per_task=["Plan 1", "Plan 2", "Plan 3"]
+            )
+            result = crew_planner._handle_crew_planning()
+            assert isinstance(result, PlannerTaskPydanticOutput)
+            assert len(result.list_of_plans_per_task) == len(crew_planner.tasks)
+            execute.assert_called_once()
+
+    def test_create_planning_agent(self, crew_planner):
+        agent = crew_planner._create_planning_agent()
+        assert isinstance(agent, Agent)
+        assert agent.role == "Task Execution Planner"
+
+    def test_create_planner_task(self, crew_planner):
+        planning_agent = Agent(
+            role="Planning Agent",
+            goal="Plan Step by Step Plan",
+            backstory="Master in Planning",
+        )
+        tasks_summary = "Summary of tasks"
+        task = crew_planner._create_planner_task(planning_agent, tasks_summary)
+
+        assert isinstance(task, Task)
+        assert task.description.startswith("Based on these tasks summary")
+        assert task.agent == planning_agent
+        assert (
+            task.expected_output
+            == "Step by step plan on how the agents can execute their tasks using the available tools with mastery"
+        )
+
+    def test_create_tasks_summary(self, crew_planner):
+        tasks_summary = crew_planner._create_tasks_summary()
+        assert isinstance(tasks_summary, str)
+        assert tasks_summary.startswith("\n                Task Number 1 - Task 1")
+        assert tasks_summary.endswith('"agent_tools": []\n                ')