mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-05-02 15:52:34 +00:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -8,4 +8,7 @@ assets/*
|
|||||||
test/
|
test/
|
||||||
docs_crew/
|
docs_crew/
|
||||||
chroma.sqlite3
|
chroma.sqlite3
|
||||||
old_en.json
|
old_en.json
|
||||||
|
db/
|
||||||
|
test.py
|
||||||
|
rc-tests/*
|
||||||
@@ -54,7 +54,7 @@ OPENAI_API_KEY=''
|
|||||||
|
|
||||||
## Ollama Integration (ex. for using Llama 2 locally)
|
## Ollama Integration (ex. for using Llama 2 locally)
|
||||||
1. [Download Ollama](https://ollama.com/download).
|
1. [Download Ollama](https://ollama.com/download).
|
||||||
2. After setting up the Ollama, Pull the Llama2 by typing following lines into the terminal ```ollama pull Llama2```.
|
2. After setting up the Ollama, Pull the Llama2 by typing following lines into the terminal ```ollama pull llama2```.
|
||||||
3. Create a ModelFile similar the one below in your project directory.
|
3. Create a ModelFile similar the one below in your project directory.
|
||||||
```
|
```
|
||||||
FROM llama2
|
FROM llama2
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ Note: Substitute 'https://docs.example.com/reference' with your target documenta
|
|||||||
By default, the tool uses OpenAI for both embeddings and summarization. To customize the model, you can use a config dictionary as follows:
|
By default, the tool uses OpenAI for both embeddings and summarization. To customize the model, you can use a config dictionary as follows:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
tool = YoutubeVideoSearchTool(
|
tool = CodeDocsSearchTool(
|
||||||
config=dict(
|
config=dict(
|
||||||
llm=dict(
|
llm=dict(
|
||||||
provider="ollama", # or google, openai, anthropic, llama2, ...
|
provider="ollama", # or google, openai, anthropic, llama2, ...
|
||||||
|
|||||||
825
poetry.lock
generated
825
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "crewai"
|
name = "crewai"
|
||||||
version = "0.30.0rc3"
|
version = "0.30.0rc6"
|
||||||
description = "Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks."
|
description = "Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks."
|
||||||
authors = ["Joao Moura <joao@crewai.com>"]
|
authors = ["Joao Moura <joao@crewai.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -23,7 +23,7 @@ opentelemetry-sdk = "^1.22.0"
|
|||||||
opentelemetry-exporter-otlp-proto-http = "^1.22.0"
|
opentelemetry-exporter-otlp-proto-http = "^1.22.0"
|
||||||
instructor = "^0.5.2"
|
instructor = "^0.5.2"
|
||||||
regex = "^2023.12.25"
|
regex = "^2023.12.25"
|
||||||
crewai-tools = { version = "^0.2.2", optional = true }
|
crewai-tools = { version = "^0.2.3", optional = true }
|
||||||
click = "^8.1.7"
|
click = "^8.1.7"
|
||||||
python-dotenv = "^1.0.0"
|
python-dotenv = "^1.0.0"
|
||||||
embedchain = "^0.1.98"
|
embedchain = "^0.1.98"
|
||||||
@@ -46,7 +46,7 @@ mkdocs-material = {extras = ["imaging"], version = "^9.5.7"}
|
|||||||
mkdocs-material-extensions = "^1.3.1"
|
mkdocs-material-extensions = "^1.3.1"
|
||||||
pillow = "^10.2.0"
|
pillow = "^10.2.0"
|
||||||
cairosvg = "^2.7.1"
|
cairosvg = "^2.7.1"
|
||||||
crewai-tools = "^0.2.2"
|
crewai-tools = "^0.2.3"
|
||||||
|
|
||||||
[tool.isort]
|
[tool.isort]
|
||||||
profile = "black"
|
profile = "black"
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class Crew(BaseModel):
|
|||||||
tasks: List of tasks assigned to the crew.
|
tasks: List of tasks assigned to the crew.
|
||||||
agents: List of agents part of this crew.
|
agents: List of agents part of this crew.
|
||||||
manager_llm: The language model that will run manager agent.
|
manager_llm: The language model that will run manager agent.
|
||||||
|
manager_agent: Custom agent that will be used as manager.
|
||||||
memory: Whether the crew should use memory to store memories of it's execution.
|
memory: Whether the crew should use memory to store memories of it's execution.
|
||||||
manager_callbacks: The callback handlers to be executed by the manager agent when hierarchical process is used
|
manager_callbacks: The callback handlers to be executed by the manager agent when hierarchical process is used
|
||||||
cache: Whether the crew should use a cache to store the results of the tools execution.
|
cache: Whether the crew should use a cache to store the results of the tools execution.
|
||||||
@@ -175,14 +176,23 @@ class Crew(BaseModel):
|
|||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def check_manager_llm(self):
|
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."""
|
||||||
if self.process == Process.hierarchical and (
|
if self.process == Process.hierarchical:
|
||||||
not self.manager_llm and not self.manager_agent
|
if not self.manager_llm and not self.manager_agent:
|
||||||
):
|
raise PydanticCustomError(
|
||||||
raise PydanticCustomError(
|
"missing_manager_llm_or_manager_agent",
|
||||||
"missing_manager_llm",
|
"Attribute `manager_llm` or `manager_agent` is required when using hierarchical process.",
|
||||||
"Attribute `manager_llm` is required when using hierarchical process.",
|
{},
|
||||||
{},
|
)
|
||||||
)
|
|
||||||
|
if (self.manager_agent is not None) and (
|
||||||
|
self.agents.count(self.manager_agent) > 0
|
||||||
|
):
|
||||||
|
raise PydanticCustomError(
|
||||||
|
"manager_agent_in_agents",
|
||||||
|
"Manager agent should not be included in agents list.",
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
@@ -316,12 +326,13 @@ class Crew(BaseModel):
|
|||||||
"""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."""
|
||||||
|
|
||||||
i18n = I18N(prompt_file=self.prompt_file)
|
i18n = I18N(prompt_file=self.prompt_file)
|
||||||
try:
|
if self.manager_agent is not None:
|
||||||
self.manager_agent.allow_delegation = (
|
self.manager_agent.allow_delegation = True
|
||||||
True # Forcing Allow delegation to the manager
|
|
||||||
)
|
|
||||||
manager = self.manager_agent
|
manager = self.manager_agent
|
||||||
except:
|
if len(manager.tools) > 0:
|
||||||
|
raise Exception("Manager agent should not have tools")
|
||||||
|
manager.tools = AgentTools(agents=self.agents).tools()
|
||||||
|
else:
|
||||||
manager = Agent(
|
manager = Agent(
|
||||||
role=i18n.retrieve("hierarchical_manager_agent", "role"),
|
role=i18n.retrieve("hierarchical_manager_agent", "role"),
|
||||||
goal=i18n.retrieve("hierarchical_manager_agent", "goal"),
|
goal=i18n.retrieve("hierarchical_manager_agent", "goal"),
|
||||||
|
|||||||
@@ -1,14 +1,28 @@
|
|||||||
tasks_order = []
|
tasks_order = []
|
||||||
|
|
||||||
|
|
||||||
|
def memoize(func):
|
||||||
|
cache = {}
|
||||||
|
|
||||||
|
def memoized_func(*args, **kwargs):
|
||||||
|
key = (args, tuple(kwargs.items()))
|
||||||
|
if key not in cache:
|
||||||
|
cache[key] = func(*args, **kwargs)
|
||||||
|
return cache[key]
|
||||||
|
|
||||||
|
return memoized_func
|
||||||
|
|
||||||
|
|
||||||
def task(func):
|
def task(func):
|
||||||
func.is_task = True
|
func.is_task = True
|
||||||
tasks_order.append(func.__name__)
|
tasks_order.append(func.__name__)
|
||||||
|
func = memoize(func)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
||||||
def agent(func):
|
def agent(func):
|
||||||
func.is_agent = True
|
func.is_agent = True
|
||||||
|
func = memoize(func)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import ast
|
import ast
|
||||||
|
from difflib import SequenceMatcher
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from typing import Any, List, Union
|
from typing import Any, List, Union
|
||||||
from difflib import SequenceMatcher
|
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool
|
from langchain_core.tools import BaseTool
|
||||||
from langchain_openai import ChatOpenAI
|
from langchain_openai import ChatOpenAI
|
||||||
@@ -330,7 +330,7 @@ class ToolUsage:
|
|||||||
|
|
||||||
return calling
|
return calling
|
||||||
|
|
||||||
def _validate_tool_input(self, tool_input: str) -> dict:
|
def _validate_tool_input(self, tool_input: str) -> str:
|
||||||
try:
|
try:
|
||||||
ast.literal_eval(tool_input)
|
ast.literal_eval(tool_input)
|
||||||
return tool_input
|
return tool_input
|
||||||
@@ -351,13 +351,17 @@ class ToolUsage:
|
|||||||
continue # Skip malformed entries
|
continue # Skip malformed entries
|
||||||
key, value = entry.split(":", 1)
|
key, value = entry.split(":", 1)
|
||||||
|
|
||||||
key = key.strip().strip(
|
# Remove extraneous white spaces and quotes, replace single quotes
|
||||||
'"'
|
key = key.strip().strip('"').replace("'", '"')
|
||||||
) # Remove extraneous white spaces and quotes
|
|
||||||
value = value.strip()
|
value = value.strip()
|
||||||
|
|
||||||
# Check and format the value based on its type
|
# Handle replacement of single quotes at the start and end of the value string
|
||||||
if value.isdigit(): # Check if value is a digit, hence integer
|
if value.startswith("'") and value.endswith("'"):
|
||||||
|
value = value[1:-1] # Remove single quotes
|
||||||
|
value = (
|
||||||
|
'"' + value.replace('"', '\\"') + '"'
|
||||||
|
) # Re-encapsulate with double quotes
|
||||||
|
elif value.isdigit(): # Check if value is a digit, hence integer
|
||||||
formatted_value = value
|
formatted_value = value
|
||||||
elif value.lower() in [
|
elif value.lower() in [
|
||||||
"true",
|
"true",
|
||||||
@@ -367,7 +371,7 @@ class ToolUsage:
|
|||||||
formatted_value = value.lower()
|
formatted_value = value.lower()
|
||||||
else:
|
else:
|
||||||
# Assume the value is a string and needs quotes
|
# Assume the value is a string and needs quotes
|
||||||
formatted_value = '"' + value.strip('"').replace('"', '\\"') + '"'
|
formatted_value = '"' + value.replace('"', '\\"') + '"'
|
||||||
|
|
||||||
# Rebuild the entry with proper quoting
|
# Rebuild the entry with proper quoting
|
||||||
formatted_entry = f'"{key}": {formatted_value}'
|
formatted_entry = f'"{key}": {formatted_value}'
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -918,8 +918,6 @@ def test_crew_log_file_output(tmp_path):
|
|||||||
def test_manager_agent():
|
def test_manager_agent():
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from langchain_openai import ChatOpenAI
|
|
||||||
|
|
||||||
task = Task(
|
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.",
|
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.",
|
expected_output="5 bullet points with a paragraph for each idea.",
|
||||||
@@ -930,7 +928,6 @@ def test_manager_agent():
|
|||||||
goal="Manage the crew and ensure the tasks are completed efficiently.",
|
goal="Manage the crew and ensure the tasks are completed efficiently.",
|
||||||
backstory="You're an experienced manager, skilled in overseeing complex projects and guiding teams to success. Your role is to coordinate the efforts of the crew members, ensuring that each task is completed on time and to the highest standard.",
|
backstory="You're an experienced manager, skilled in overseeing complex projects and guiding teams to success. Your role is to coordinate the efforts of the crew members, ensuring that each task is completed on time and to the highest standard.",
|
||||||
allow_delegation=False,
|
allow_delegation=False,
|
||||||
llm=ChatOpenAI(temperature=0, model="gpt-4"),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
crew = Crew(
|
crew = Crew(
|
||||||
@@ -944,3 +941,53 @@ def test_manager_agent():
|
|||||||
crew.kickoff()
|
crew.kickoff()
|
||||||
assert manager.allow_delegation == True
|
assert manager.allow_delegation == True
|
||||||
execute.assert_called()
|
execute.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_manager_agent_in_agents_raises_exception():
|
||||||
|
pass
|
||||||
|
|
||||||
|
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.",
|
||||||
|
)
|
||||||
|
|
||||||
|
manager = Agent(
|
||||||
|
role="Manager",
|
||||||
|
goal="Manage the crew and ensure the tasks are completed efficiently.",
|
||||||
|
backstory="You're an experienced manager, skilled in overseeing complex projects and guiding teams to success. Your role is to coordinate the efforts of the crew members, ensuring that each task is completed on time and to the highest standard.",
|
||||||
|
allow_delegation=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(pydantic_core._pydantic_core.ValidationError):
|
||||||
|
crew = Crew(
|
||||||
|
agents=[researcher, writer, manager],
|
||||||
|
process=Process.hierarchical,
|
||||||
|
manager_agent=manager,
|
||||||
|
tasks=[task],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_manager_agent_with_tools_raises_exception():
|
||||||
|
pass
|
||||||
|
|
||||||
|
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.",
|
||||||
|
)
|
||||||
|
|
||||||
|
manager = Agent(
|
||||||
|
role="Manager",
|
||||||
|
goal="Manage the crew and ensure the tasks are completed efficiently.",
|
||||||
|
backstory="You're an experienced manager, skilled in overseeing complex projects and guiding teams to success. Your role is to coordinate the efforts of the crew members, ensuring that each task is completed on time and to the highest standard.",
|
||||||
|
allow_delegation=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
crew = Crew(
|
||||||
|
agents=[researcher, writer],
|
||||||
|
process=Process.hierarchical,
|
||||||
|
manager_agent=manager,
|
||||||
|
tasks=[task],
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
crew.kickoff()
|
||||||
|
|||||||
35
tests/project_test.py
Normal file
35
tests/project_test.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
from crewai.agent import Agent
|
||||||
|
from crewai.project import agent, task
|
||||||
|
from crewai.task import Task
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleCrew:
|
||||||
|
@agent
|
||||||
|
def simple_agent(self):
|
||||||
|
return Agent(
|
||||||
|
role="Simple Agent", goal="Simple Goal", backstory="Simple Backstory"
|
||||||
|
)
|
||||||
|
|
||||||
|
@task
|
||||||
|
def simple_task(self):
|
||||||
|
return Task(description="Simple Description", expected_output="Simple Output")
|
||||||
|
|
||||||
|
|
||||||
|
def test_agent_memoization():
|
||||||
|
crew = SimpleCrew()
|
||||||
|
first_call_result = crew.simple_agent()
|
||||||
|
second_call_result = crew.simple_agent()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
first_call_result is second_call_result
|
||||||
|
), "Agent memoization is not working as expected"
|
||||||
|
|
||||||
|
|
||||||
|
def test_task_memoization():
|
||||||
|
crew = SimpleCrew()
|
||||||
|
first_call_result = crew.simple_task()
|
||||||
|
second_call_result = crew.simple_task()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
first_call_result is second_call_result
|
||||||
|
), "Task memoization is not working as expected"
|
||||||
Reference in New Issue
Block a user