Skip to content

Commit

Permalink
Update flows cli to allow you to easily add additional crews to a flow
Browse files Browse the repository at this point in the history
  • Loading branch information
bhancockio committed Oct 28, 2024
1 parent 26afee9 commit 0e36d35
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 4 deletions.
34 changes: 34 additions & 0 deletions docs/concepts/flows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,40 @@ uv run kickoff

The flow will execute, and you should see the output in the console.


### Adding Additional Crews Using the CLI

Once you have created your initial flow, you can easily add additional crews to your project using the CLI. This allows you to expand your flow's capabilities by integrating new crews without starting from scratch.

To add a new crew to your existing flow, use the following command:

```bash
crewai flow add-crew <crew_name>
```

This command will create a new directory for your crew within the `crews` folder of your flow project. It will include the necessary configuration files and a crew definition file, similar to the initial setup.

#### Folder Structure

After adding a new crew, your folder structure will look like this:

name_of_flow/
├── crews/
│ ├── poem_crew/
│ │ ├── config/
│ │ │ ├── agents.yaml
│ │ │ └── tasks.yaml
│ │ └── poem_crew.py
│ └── name_of_crew/
│ ├── config/
│ │ ├── agents.yaml
│ │ └── tasks.yaml
│ └── name_of_crew.py

You can then customize the `agents.yaml` and `tasks.yaml` files to define the agents and tasks for your new crew. The `name_of_crew.py` file will contain the crew's logic, which you can modify to suit your needs.

By using the CLI to add additional crews, you can efficiently build complex AI workflows that leverage multiple crews working together.

## Plot Flows

Visualizing your AI workflows can provide valuable insights into the structure and execution paths of your flows. CrewAI offers a powerful visualization tool that allows you to generate interactive plots of your flows, making it easier to understand and optimize your AI workflows.
Expand Down
72 changes: 72 additions & 0 deletions src/crewai/cli/add_crew_to_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from pathlib import Path

import click

from .create_crew import copy_template


def add_crew_to_flow(crew_name: str) -> None:
"""Add a new crew to the current flow."""
# Check if pyproject.toml exists in the current directory
if not Path("pyproject.toml").exists():
print("This command must be run from the root of a flow project.")
raise click.ClickException(
"This command must be run from the root of a flow project."
)

# Determine the flow folder based on the current directory
flow_folder = Path.cwd()
crews_folder = flow_folder / "src" / flow_folder.name / "crews"

if not crews_folder.exists():
print("Crews folder does not exist in the current flow.")
raise click.ClickException("Crews folder does not exist in the current flow.")

# Create the crew within the flow's crews directory
create_embedded_crew(crew_name, parent_folder=crews_folder)

click.secho(
f"Crew {crew_name} added to the current flow successfully!",
fg="green",
bold=True,
)


def create_embedded_crew(crew_name: str, parent_folder: Path) -> None:
"""Create a new crew within an existing flow project."""
folder_name = crew_name.replace(" ", "_").replace("-", "_").lower()
class_name = crew_name.replace("_", " ").replace("-", " ").title().replace(" ", "")

crew_folder = parent_folder / folder_name

if crew_folder.exists():
if not click.confirm(
f"Crew {folder_name} already exists. Do you want to override it?"
):
click.secho("Operation cancelled.", fg="yellow")
return
click.secho(f"Overriding crew {folder_name}...", fg="green", bold=True)
else:
click.secho(f"Creating crew {folder_name}...", fg="green", bold=True)
crew_folder.mkdir(parents=True)

# Create config and crew.py files
config_folder = crew_folder / "config"
config_folder.mkdir(exist_ok=True)

templates_dir = Path(__file__).parent / "templates" / "crew"
config_template_files = ["agents.yaml", "tasks.yaml"]
crew_template_file = f"{folder_name}_crew.py" # Updated file name

for file_name in config_template_files:
src_file = templates_dir / "config" / file_name
dst_file = config_folder / file_name
copy_template(src_file, dst_file, crew_name, class_name, folder_name)

src_file = templates_dir / "crew.py"
dst_file = crew_folder / crew_template_file
copy_template(src_file, dst_file, crew_name, class_name, folder_name)

click.secho(
f"Crew {crew_name} added to the flow successfully!", fg="green", bold=True
)
19 changes: 15 additions & 4 deletions src/crewai/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import click
import pkg_resources

from crewai.cli.add_crew_to_flow import add_crew_to_flow
from crewai.cli.create_crew import create_crew
from crewai.cli.create_flow import create_flow
from crewai.cli.create_pipeline import create_pipeline
Expand Down Expand Up @@ -178,10 +179,12 @@ def test(n_iterations: int, model: str):
evaluate_crew(n_iterations, model)


@crewai.command(context_settings=dict(
ignore_unknown_options=True,
allow_extra_args=True,
))
@crewai.command(
context_settings=dict(
ignore_unknown_options=True,
allow_extra_args=True,
)
)
@click.pass_context
def install(context):
"""Install the Crew."""
Expand Down Expand Up @@ -324,5 +327,13 @@ def flow_plot():
plot_flow()


@flow.command(name="add-crew")
@click.argument("crew_name")
def flow_add_crew(crew_name):
"""Add a crew to an existing flow."""
click.echo(f"Adding crew {crew_name} to the flow")
add_crew_to_flow(crew_name)


if __name__ == "__main__":
crewai()
51 changes: 51 additions & 0 deletions tests/cli/cli_test.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
from pathlib import Path
from unittest import mock

import pytest
from click.testing import CliRunner

from crewai.cli.cli import (
deploy_create,
deploy_list,
deploy_logs,
deploy_push,
deploy_remove,
deply_status,
flow_add_crew,
reset_memories,
signup,
test,
Expand Down Expand Up @@ -277,3 +280,51 @@ def test_deploy_remove_no_uuid(command, runner):

assert result.exit_code == 0
mock_deploy.remove_crew.assert_called_once_with(uuid=None)


@mock.patch("crewai.cli.add_crew_to_flow.create_crew")
def test_add_crew_to_flow(create_crew_mock, runner):
# Simulate being in the root of a flow project
with mock.patch("pathlib.Path.cwd") as mock_cwd:
mock_cwd.return_value = Path("/path/to/flow_project")
with mock.patch("pathlib.Path.exists") as mock_exists:

def exists_side_effect(self):
if self == Path("/path/to/flow_project/pyproject.toml"):
return True
if self == Path("/path/to/flow_project/src/flow_project/crews"):
return True
return False

mock_exists.side_effect = exists_side_effect

result = runner.invoke(flow_add_crew, ["new_crew"])

create_crew_mock.assert_called_once_with(
"new_crew",
parent_folder=Path("/path/to/flow_project/src/flow_project/crews"),
)
assert result.exit_code == 0
assert (
"Crew new_crew added to the current flow successfully!"
in result.exception
)


def test_add_crew_to_flow_not_in_root(runner):
# Simulate not being in the root of a flow project
with mock.patch("pathlib.Path.exists", autospec=True) as mock_exists:
# Mock Path.exists to return False when checking for pyproject.toml
def exists_side_effect(self):
if self.name == "pyproject.toml":
return False # Simulate that pyproject.toml does not exist
return True # All other paths exist

mock_exists.side_effect = exists_side_effect

result = runner.invoke(flow_add_crew, ["new_crew"])

assert result.exit_code != 0
assert "This command must be run from the root of a flow project." in str(
result.output
)

0 comments on commit 0e36d35

Please sign in to comment.