Compare commits

..

3 Commits

Author SHA1 Message Date
Eduardo Chiarotti
3ae4cf3b7b Create stale.yml 2024-08-08 11:48:53 -03:00
Eduardo Chiarotti
297dc93fb4 feat: add cli to run the crew (#1080)
* feat: add cli to run the crew

* feat: change command to run_crew

* feat: change pyprojet to run_Crew

* docs: change docs to address crewai run
2024-08-08 10:48:22 -03:00
Lorenze Jay
86c6760f58 Fix logging types to bool (#1051)
* fixes pydantic validations hierarchical

* more tests

* logger logs everything or not

* verbose rm levels to bool

* updated readme verbose levels
2024-08-07 10:31:18 -07:00
24 changed files with 15923 additions and 155 deletions

View File

@@ -1,93 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: '28 20 * * 1'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: python
build-mode: none
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

26
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Mark stale issues and pull requests
on:
schedule:
- cron: '10 12 * * *'
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-label: 'no-issue-activity'
stale-issue-message: 'This issue is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.'
days-before-issue-stale: 30
days-before-issue-close: 5
stale-pr-label: 'no-pr-activity'
stale-pr-message: 'This PR is stale because it has been open for 45 days with no activity.'
days-before-pr-stale: 45
days-before-pr-close: -1

View File

@@ -126,7 +126,7 @@ task2 = Task(
crew = Crew(
agents=[researcher, writer],
tasks=[task1, task2],
verbose=2, # You can set it to 1 or 2 to different logging levels
verbose=True,
process = Process.sequential
)

View File

@@ -134,7 +134,7 @@ Once a crew has been executed, its output can be accessed through the `output` a
crew = Crew(
agents=[research_agent, writer_agent],
tasks=[research_task, write_article_task],
verbose=2
verbose=True
)
crew_output = crew.kickoff()

View File

@@ -90,7 +90,7 @@ task = Task(
crew = Crew(
agents=[research_agent],
tasks=[task],
verbose=2
verbose=True
)
result = crew.kickoff()
@@ -142,7 +142,7 @@ task = Task(
crew = Crew(
agents=[research_agent],
tasks=[task],
verbose=2
verbose=True
)
result = crew.kickoff()
@@ -264,7 +264,7 @@ task1 = Task(
crew = Crew(
agents=[research_agent],
tasks=[task1, task2, task3],
verbose=2
verbose=True
)
result = crew.kickoff()

View File

@@ -84,7 +84,7 @@ write = Task(
crew = Crew(
agents=[researcher, writer],
tasks=[research, write],
verbose=2
verbose=True
)
# Execute tasks

View File

@@ -244,6 +244,10 @@ def run():
To run your project, use the following command:
```shell
$ crewai run
```
or
```shell
$ poetry run my_project
```

View File

@@ -79,7 +79,7 @@ task3 = Task(
crew = Crew(
agents=[data_fetcher_agent, data_processor_agent, summary_generator_agent],
tasks=[task1, conditional_task, task3],
verbose=2,
verbose=True,
)
result = crew.kickoff()

View File

@@ -81,7 +81,7 @@ task2 = Task(
crew = Crew(
agents=[researcher, writer],
tasks=[task1, task2],
verbose=2,
verbose=True,
memory=True,
)

View File

@@ -74,7 +74,7 @@ task = Task(description="""what is 3 + 5""",
crew = Crew(
agents=[general_agent],
tasks=[task],
verbose=2
verbose=True
)
result = crew.kickoff()

View File

@@ -158,7 +158,7 @@ class BaseAgent(ABC, BaseModel):
@model_validator(mode="after")
def set_private_attrs(self):
"""Set private attributes."""
self._logger = Logger(self.verbose)
self._logger = Logger(verbose=self.verbose)
if self.max_rpm and not self._rpm_controller:
self._rpm_controller = RPMController(
max_rpm=self.max_rpm, logger=self._logger

View File

@@ -51,7 +51,7 @@ class CrewAgentExecutor(AgentExecutor, CrewAgentExecutorMixin):
system_template: Optional[str] = None
prompt_template: Optional[str] = None
response_template: Optional[str] = None
_logger: Logger = Logger(verbose_level=2)
_logger: Logger = Logger()
_fit_context_window_strategy: Optional[Literal["summarize"]] = "summarize"
def _call(

View File

@@ -9,6 +9,7 @@ from .create_crew import create_crew
from .evaluate_crew import evaluate_crew
from .replay_from_task import replay_task_command
from .reset_memories_command import reset_memories_command
from .run_crew import run_crew
from .train_crew import train_crew
@@ -147,5 +148,12 @@ def test(n_iterations: int, model: str):
evaluate_crew(n_iterations, model)
@crewai.command()
def run():
"""Run the crew."""
click.echo("Running the crew")
run_crew()
if __name__ == "__main__":
crewai()

View File

@@ -0,0 +1,23 @@
import subprocess
import click
def run_crew() -> None:
"""
Run the crew by running a command in the Poetry environment.
"""
command = ["poetry", "run", "run_crew"]
try:
result = subprocess.run(command, capture_output=False, text=True, check=True)
if result.stderr:
click.echo(result.stderr, err=True)
except subprocess.CalledProcessError as e:
click.echo(f"An error occurred while running the crew: {e}", err=True)
click.echo(e.output, err=True)
except Exception as e:
click.echo(f"An unexpected error occurred: {e}", err=True)

View File

@@ -34,6 +34,10 @@ poetry install
To kickstart your crew of AI agents and begin task execution, run this from the root folder of your project:
```bash
$ crewai run
```
or
```bash
poetry run {{folder_name}}
```

View File

@@ -48,6 +48,6 @@ class {{crew_name}}Crew():
agents=self.agents, # Automatically created by the @agent decorator
tasks=self.tasks, # Automatically created by the @task decorator
process=Process.sequential,
verbose=2,
verbose=True,
# process=Process.hierarchical, # In case you wanna use that instead https://docs.crewai.com/how-to/Hierarchical/
)

View File

@@ -10,6 +10,7 @@ crewai = { extras = ["tools"], version = "^0.46.0" }
[tool.poetry.scripts]
{{folder_name}} = "{{folder_name}}.main:run"
run_crew = "{{folder_name}}.main:run"
train = "{{folder_name}}.main:train"
replay = "{{folder_name}}.main:replay"
test = "{{folder_name}}.main:test"

View File

@@ -102,7 +102,7 @@ class Crew(BaseModel):
tasks: List[Task] = Field(default_factory=list)
agents: List[BaseAgent] = Field(default_factory=list)
process: Process = Field(default=Process.sequential)
verbose: Union[int, bool] = Field(default=0)
verbose: bool = Field(default=False)
memory: bool = Field(
default=False,
description="Whether the crew should use memory to store memories of it's execution",
@@ -196,7 +196,7 @@ class Crew(BaseModel):
def set_private_attrs(self) -> "Crew":
"""Set private attributes."""
self._cache_handler = CacheHandler()
self._logger = Logger(self.verbose)
self._logger = Logger(verbose=self.verbose)
if self.output_log_file:
self._file_handler = FileHandler(self.output_log_file)
self._rpm_controller = RPMController(max_rpm=self.max_rpm, logger=self._logger)

View File

@@ -6,15 +6,11 @@ from crewai.utilities.printer import Printer
class Logger:
_printer = Printer()
def __init__(self, verbose_level=0):
verbose_level = (
2 if isinstance(verbose_level, bool) and verbose_level else verbose_level
)
self.verbose_level = verbose_level
def __init__(self, verbose=False):
self.verbose = verbose
def log(self, level, message, color="bold_green"):
level_map = {"debug": 1, "info": 2}
if self.verbose_level and level_map.get(level, 0) <= self.verbose_level:
if self.verbose:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self._printer.print(
f"[{timestamp}][{level.upper()}]: {message}", color=color

View File

@@ -470,7 +470,7 @@ def test_agent_respect_the_max_rpm_set_over_crew_rpm(capsys):
agent=agent,
)
crew = Crew(agents=[agent], tasks=[task], max_rpm=1, verbose=2)
crew = Crew(agents=[agent], tasks=[task], max_rpm=1, verbose=True)
with patch.object(RPMController, "_wait_for_next_minute") as moveon:
moveon.return_value = True
@@ -522,7 +522,7 @@ def test_agent_without_max_rpm_respet_crew_rpm(capsys):
),
]
crew = Crew(agents=[agent1, agent2], tasks=tasks, max_rpm=1, verbose=2)
crew = Crew(agents=[agent1, agent2], tasks=tasks, max_rpm=1, verbose=True)
with patch.object(RPMController, "_wait_for_next_minute") as moveon:
moveon.return_value = True
@@ -563,7 +563,7 @@ def test_agent_error_on_parsing_tool(capsys):
crew = Crew(
agents=[agent1],
tasks=tasks,
verbose=2,
verbose=True,
function_calling_llm=ChatOpenAI(model="gpt-4-0125-preview"),
)
@@ -602,7 +602,7 @@ def test_agent_remembers_output_format_after_using_tools_too_many_times():
)
]
crew = Crew(agents=[agent1], tasks=tasks, verbose=2)
crew = Crew(agents=[agent1], tasks=tasks, verbose=True)
with patch.object(ToolUsage, "_remember_format") as remember_format:
crew.kickoff()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -449,45 +449,13 @@ def test_crew_verbose_output(capsys):
assert expected_string in captured.out
# Now test with verbose set to False
crew._logger = Logger(verbose_level=False)
crew.verbose = False
crew._logger = Logger(verbose=False)
crew.kickoff()
captured = capsys.readouterr()
assert captured.out == ""
@pytest.mark.vcr(filter_headers=["authorization"])
def test_crew_verbose_levels_output(capsys):
tasks = [
Task(
description="Write about AI advancements.",
expected_output="A 4 paragraph article about AI.",
agent=researcher,
)
]
crew = Crew(agents=[researcher], tasks=tasks, process=Process.sequential, verbose=1)
crew.kickoff()
captured = capsys.readouterr()
expected_strings = ["Working Agent: Researcher", "[Researcher] Task output:"]
for expected_string in expected_strings:
assert expected_string in captured.out
# Now test with verbose set to 2
crew._logger = Logger(verbose_level=2)
crew.kickoff()
captured = capsys.readouterr()
expected_strings = [
"Working Agent: Researcher",
"Starting Task: Write about AI advancements.",
"[Researcher] Task output:",
]
for expected_string in expected_strings:
assert expected_string in captured.out
@pytest.mark.vcr(filter_headers=["authorization"])
def test_cache_hitting_between_agents():
from unittest.mock import call, patch
@@ -561,7 +529,7 @@ def test_api_calls_throttling(capsys):
agent=agent,
)
crew = Crew(agents=[agent], tasks=[task], max_rpm=2, verbose=2)
crew = Crew(agents=[agent], tasks=[task], max_rpm=2, verbose=True)
with patch.object(RPMController, "_wait_for_next_minute") as moveon:
moveon.return_value = True
@@ -622,7 +590,7 @@ def test_agents_rpm_is_never_set_if_crew_max_RPM_is_not_set():
agent=agent,
)
Crew(agents=[agent], tasks=[task], verbose=2)
Crew(agents=[agent], tasks=[task], verbose=True)
assert agent._rpm_controller is None
@@ -2568,3 +2536,49 @@ def test_crew_testing_function(mock_kickoff, crew_evaluator):
mock.call().print_crew_evaluation_result(),
]
)
@pytest.mark.vcr(filter_headers=["authorization"])
def test_hierarchical_verbose_manager_agent():
from langchain_openai import ChatOpenAI
task = Task(
description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.",
expected_output="5 bullet points with a paragraph for each idea.",
)
crew = Crew(
agents=[researcher, writer],
tasks=[task],
process=Process.hierarchical,
manager_llm=ChatOpenAI(temperature=0, model="gpt-4o"),
verbose=True,
)
crew.kickoff()
assert crew.manager_agent is not None
assert crew.manager_agent.verbose
@pytest.mark.vcr(filter_headers=["authorization"])
def test_hierarchical_verbose_false_manager_agent():
from langchain_openai import ChatOpenAI
task = Task(
description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.",
expected_output="5 bullet points with a paragraph for each idea.",
)
crew = Crew(
agents=[researcher, writer],
tasks=[task],
process=Process.hierarchical,
manager_llm=ChatOpenAI(temperature=0, model="gpt-4o"),
verbose=False,
)
crew.kickoff()
assert crew.manager_agent is not None
assert not crew.manager_agent.verbose

View File

@@ -434,7 +434,7 @@ def test_output_pydantic_to_another_task():
agent=scorer,
)
crew = Crew(agents=[scorer], tasks=[task1, task2], verbose=2)
crew = Crew(agents=[scorer], tasks=[task1, task2], verbose=True)
result = crew.kickoff()
pydantic_result = result.pydantic
assert isinstance(