Compare commits

...

7 Commits

Author SHA1 Message Date
Brandon Hancock
44217b1e5b add back in docstring 2024-10-25 17:58:45 -04:00
Brandon Hancock
430a2038a3 Drop print 2024-10-25 16:02:31 -04:00
Brandon Hancock
6df3758770 fix lint 2024-10-25 15:42:41 -04:00
Brandon Hancock
05cd56ca54 improve tool text descriptoin and args 2024-10-25 15:37:38 -04:00
Vini Brasil
f29f4abdd7 Forward install command options to uv sync (#1510)
Allow passing additional options from `crewai install` directly to
`uv sync`. This enables commands like `crewai install --locked` to work
as expected by forwarding all flags and options to the underlying uv
command.
2024-10-25 11:20:41 -03:00
Eduardo Chiarotti
4589d6fe9d feat: add tomli so we can support 3.10 (#1506)
* feat: add tomli so we can support 3.10

* feat: add validation for poetry data
2024-10-25 10:33:21 -03:00
Brandon Hancock (bhancock_ai)
201e652fa2 update plot command (#1504) 2024-10-24 14:44:30 -04:00
9 changed files with 59 additions and 49 deletions

View File

@@ -28,6 +28,7 @@ dependencies = [
"uv>=0.4.25", "uv>=0.4.25",
"tomli-w>=1.1.0", "tomli-w>=1.1.0",
"chromadb>=0.4.24", "chromadb>=0.4.24",
"tomli>=2.0.2",
] ]
[project.urls] [project.urls]

View File

@@ -1,7 +1,6 @@
import os import os
import shutil import shutil
import subprocess import subprocess
from inspect import signature
from typing import Any, List, Literal, Optional, Union from typing import Any, List, Literal, Optional, Union
from pydantic import Field, InstanceOf, PrivateAttr, model_validator from pydantic import Field, InstanceOf, PrivateAttr, model_validator
@@ -395,26 +394,26 @@ class Agent(BaseAgent):
def _render_text_description_and_args(self, tools: List[Any]) -> str: def _render_text_description_and_args(self, tools: List[Any]) -> str:
"""Render the tool name, description, and args in plain text. """Render the tool name, description, and args in plain text.
Output will be in the format of: Output will be in the format of:
.. code-block:: markdown .. code-block:: markdown
search: This tool is used for search, args: {"query": {"type": "string"}} search: This tool is used for search, args: {"query": {"type": "string"}}
calculator: This tool is used for math, \ calculator: This tool is used for math, \
args: {"expression": {"type": "string"}} args: {"expression": {"type": "string"}}
""" """
tool_strings = [] tool_strings = []
for tool in tools: for tool in tools:
args_schema = str(tool.model_fields) args_schema = {
if hasattr(tool, "func") and tool.func: name: {
sig = signature(tool.func) "description": field.description,
description = ( "type": field.annotation.__name__,
f"Tool Name: {tool.name}{sig}\nTool Description: {tool.description}" }
) for name, field in tool.args_schema.model_fields.items()
else: }
description = ( description = (
f"Tool Name: {tool.name}\nTool Description: {tool.description}" f"Tool Name: {tool.name}\nTool Description: {tool.description}"
) )
tool_strings.append(f"{description}\nTool Arguments: {args_schema}") tool_strings.append(f"{description}\nTool Arguments: {args_schema}")
return "\n".join(tool_strings) return "\n".join(tool_strings)

View File

@@ -178,10 +178,14 @@ def test(n_iterations: int, model: str):
evaluate_crew(n_iterations, model) evaluate_crew(n_iterations, model)
@crewai.command() @crewai.command(context_settings=dict(
def install(): ignore_unknown_options=True,
allow_extra_args=True,
))
@click.pass_context
def install(context):
"""Install the Crew.""" """Install the Crew."""
install_crew() install_crew(context.args)
@crewai.command() @crewai.command()

View File

@@ -3,12 +3,13 @@ import subprocess
import click import click
def install_crew() -> None: def install_crew(proxy_options: list[str]) -> None:
""" """
Install the crew by running the UV command to lock and install. Install the crew by running the UV command to lock and install.
""" """
try: try:
subprocess.run(["uv", "sync"], check=True, capture_output=False, text=True) command = ["uv", "sync"] + proxy_options
subprocess.run(command, check=True, capture_output=False, text=True)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
click.echo(f"An error occurred while running the crew: {e}", err=True) click.echo(f"An error occurred while running the crew: {e}", err=True)

View File

@@ -7,7 +7,7 @@ def plot_flow() -> None:
""" """
Plot the flow by running a command in the UV environment. Plot the flow by running a command in the UV environment.
""" """
command = ["uv", "run", "plot_flow"] command = ["uv", "run", "plot"]
try: try:
result = subprocess.run(command, capture_output=False, text=True, check=True) result = subprocess.run(command, capture_output=False, text=True, check=True)

View File

@@ -1,10 +1,9 @@
import subprocess import subprocess
import click import click
import tomllib
from packaging import version from packaging import version
from crewai.cli.utils import get_crewai_version from crewai.cli.utils import get_crewai_version, read_toml
def run_crew() -> None: def run_crew() -> None:
@@ -15,10 +14,9 @@ def run_crew() -> None:
crewai_version = get_crewai_version() crewai_version = get_crewai_version()
min_required_version = "0.71.0" min_required_version = "0.71.0"
with open("pyproject.toml", "rb") as f: pyproject_data = read_toml()
data = tomllib.load(f)
if data.get("tool", {}).get("poetry") and ( if pyproject_data.get("tool", {}).get("poetry") and (
version.parse(crewai_version) < version.parse(min_required_version) version.parse(crewai_version) < version.parse(min_required_version)
): ):
click.secho( click.secho(
@@ -35,10 +33,7 @@ def run_crew() -> None:
click.echo(f"An error occurred while running the crew: {e}", err=True) click.echo(f"An error occurred while running the crew: {e}", err=True)
click.echo(e.output, err=True, nl=True) click.echo(e.output, err=True, nl=True)
with open("pyproject.toml", "rb") as f: if pyproject_data.get("tool", {}).get("poetry"):
data = tomllib.load(f)
if data.get("tool", {}).get("poetry"):
click.secho( click.secho(
"It's possible that you are using an old version of crewAI that uses poetry, please run `crewai update` to update your pyproject.toml to use uv.", "It's possible that you are using an old version of crewAI that uses poetry, please run `crewai update` to update your pyproject.toml to use uv.",
fg="yellow", fg="yellow",

View File

@@ -2,7 +2,8 @@ import os
import shutil import shutil
import tomli_w import tomli_w
import tomllib
from crewai.cli.utils import read_toml
def update_crew() -> None: def update_crew() -> None:
@@ -18,10 +19,9 @@ def migrate_pyproject(input_file, output_file):
And it will be used to migrate the pyproject.toml to the new format when uv is used. And it will be used to migrate the pyproject.toml to the new format when uv is used.
When the time comes that uv supports the new format, this function will be deprecated. When the time comes that uv supports the new format, this function will be deprecated.
""" """
poetry_data = {}
# Read the input pyproject.toml # Read the input pyproject.toml
with open(input_file, "rb") as f: pyproject_data = read_toml()
pyproject = tomllib.load(f)
# Initialize the new project structure # Initialize the new project structure
new_pyproject = { new_pyproject = {
@@ -30,30 +30,30 @@ def migrate_pyproject(input_file, output_file):
} }
# Migrate project metadata # Migrate project metadata
if "tool" in pyproject and "poetry" in pyproject["tool"]: if "tool" in pyproject_data and "poetry" in pyproject_data["tool"]:
poetry = pyproject["tool"]["poetry"] poetry_data = pyproject_data["tool"]["poetry"]
new_pyproject["project"]["name"] = poetry.get("name") new_pyproject["project"]["name"] = poetry_data.get("name")
new_pyproject["project"]["version"] = poetry.get("version") new_pyproject["project"]["version"] = poetry_data.get("version")
new_pyproject["project"]["description"] = poetry.get("description") new_pyproject["project"]["description"] = poetry_data.get("description")
new_pyproject["project"]["authors"] = [ new_pyproject["project"]["authors"] = [
{ {
"name": author.split("<")[0].strip(), "name": author.split("<")[0].strip(),
"email": author.split("<")[1].strip(">").strip(), "email": author.split("<")[1].strip(">").strip(),
} }
for author in poetry.get("authors", []) for author in poetry_data.get("authors", [])
] ]
new_pyproject["project"]["requires-python"] = poetry.get("python") new_pyproject["project"]["requires-python"] = poetry_data.get("python")
else: else:
# If it's already in the new format, just copy the project section # If it's already in the new format, just copy the project section
new_pyproject["project"] = pyproject.get("project", {}) new_pyproject["project"] = pyproject_data.get("project", {})
# Migrate or copy dependencies # Migrate or copy dependencies
if "dependencies" in new_pyproject["project"]: if "dependencies" in new_pyproject["project"]:
# If dependencies are already in the new format, keep them as is # If dependencies are already in the new format, keep them as is
pass pass
elif "dependencies" in poetry: elif poetry_data and "dependencies" in poetry_data:
new_pyproject["project"]["dependencies"] = [] new_pyproject["project"]["dependencies"] = []
for dep, version in poetry["dependencies"].items(): for dep, version in poetry_data["dependencies"].items():
if isinstance(version, dict): # Handle extras if isinstance(version, dict): # Handle extras
extras = ",".join(version.get("extras", [])) extras = ",".join(version.get("extras", []))
new_dep = f"{dep}[{extras}]" new_dep = f"{dep}[{extras}]"
@@ -67,10 +67,10 @@ def migrate_pyproject(input_file, output_file):
new_pyproject["project"]["dependencies"].append(new_dep) new_pyproject["project"]["dependencies"].append(new_dep)
# Migrate or copy scripts # Migrate or copy scripts
if "scripts" in poetry: if poetry_data and "scripts" in poetry_data:
new_pyproject["project"]["scripts"] = poetry["scripts"] new_pyproject["project"]["scripts"] = poetry_data["scripts"]
elif "scripts" in pyproject.get("project", {}): elif pyproject_data.get("project", {}) and "scripts" in pyproject_data["project"]:
new_pyproject["project"]["scripts"] = pyproject["project"]["scripts"] new_pyproject["project"]["scripts"] = pyproject_data["project"]["scripts"]
else: else:
new_pyproject["project"]["scripts"] = {} new_pyproject["project"]["scripts"] = {}
@@ -87,8 +87,8 @@ def migrate_pyproject(input_file, output_file):
new_pyproject["project"]["scripts"]["run_crew"] = f"{module_name}.main:run" new_pyproject["project"]["scripts"]["run_crew"] = f"{module_name}.main:run"
# Migrate optional dependencies # Migrate optional dependencies
if "extras" in poetry: if poetry_data and "extras" in poetry_data:
new_pyproject["project"]["optional-dependencies"] = poetry["extras"] new_pyproject["project"]["optional-dependencies"] = poetry_data["extras"]
# Backup the old pyproject.toml # Backup the old pyproject.toml
backup_file = "pyproject-old.toml" backup_file = "pyproject-old.toml"

View File

@@ -6,6 +6,7 @@ from functools import reduce
from typing import Any, Dict, List from typing import Any, Dict, List
import click import click
import tomli
from rich.console import Console from rich.console import Console
from crewai.cli.authentication.utils import TokenManager from crewai.cli.authentication.utils import TokenManager
@@ -54,6 +55,13 @@ def simple_toml_parser(content):
return result return result
def read_toml(file_path: str = "pyproject.toml"):
"""Read the content of a TOML file and return it as a dictionary."""
with open(file_path, "rb") as f:
toml_dict = tomli.load(f)
return toml_dict
def parse_toml(content): def parse_toml(content):
if sys.version_info >= (3, 11): if sys.version_info >= (3, 11):
return tomllib.loads(content) return tomllib.loads(content)

2
uv.lock generated
View File

@@ -625,6 +625,7 @@ dependencies = [
{ name = "python-dotenv" }, { name = "python-dotenv" },
{ name = "pyvis" }, { name = "pyvis" },
{ name = "regex" }, { name = "regex" },
{ name = "tomli" },
{ name = "tomli-w" }, { name = "tomli-w" },
{ name = "uv" }, { name = "uv" },
] ]
@@ -679,6 +680,7 @@ requires-dist = [
{ name = "python-dotenv", specifier = ">=1.0.0" }, { name = "python-dotenv", specifier = ">=1.0.0" },
{ name = "pyvis", specifier = ">=0.3.2" }, { name = "pyvis", specifier = ">=0.3.2" },
{ name = "regex", specifier = ">=2024.9.11" }, { name = "regex", specifier = ">=2024.9.11" },
{ name = "tomli", specifier = ">=2.0.2" },
{ name = "tomli-w", specifier = ">=1.1.0" }, { name = "tomli-w", specifier = ">=1.1.0" },
{ name = "uv", specifier = ">=0.4.25" }, { name = "uv", specifier = ">=0.4.25" },
] ]