mirror of
https://github.com/crewAIInc/crewAI.git
synced 2025-12-16 04:18:35 +00:00
feat: add crewai uv wrapper for uv commands (#3581)
This commit is contained in:
@@ -404,6 +404,10 @@ crewai config reset
|
|||||||
After resetting configuration, re-run `crewai login` to authenticate again.
|
After resetting configuration, re-run `crewai login` to authenticate again.
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
CrewAI CLI handles authentication to the Tool Repository automatically when adding packages to your project. Just append `crewai` before any `uv` command to use it. E.g. `crewai uv add requests`. For more information, see [Tool Repository](https://docs.crewai.com/enterprise/features/tool-repository) docs.
|
||||||
|
</Tip>
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
Configuration settings are stored in `~/.config/crewai/settings.json`. Some settings like organization name and UUID are read-only and managed through authentication and organization commands. Tool repository related settings are hidden and cannot be set directly by users.
|
Configuration settings are stored in `~/.config/crewai/settings.json`. Some settings like organization name and UUID are read-only and managed through authentication and organization commands. Tool repository related settings are hidden and cannot be set directly by users.
|
||||||
</Note>
|
</Note>
|
||||||
|
|||||||
@@ -52,6 +52,36 @@ researcher = Agent(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Adding other packages after installing a tool
|
||||||
|
|
||||||
|
After installing a tool from the CrewAI Enterprise Tool Repository, you need to use the `crewai uv` command to add other packages to your project.
|
||||||
|
Using pure `uv` commands will fail due to authentication to tool repository being handled by the CLI. By using the `crewai uv` command, you can add other packages to your project without having to worry about authentication.
|
||||||
|
Any `uv` command can be used with the `crewai uv` command, making it a powerful tool for managing your project's dependencies without the hassle of managing authentication through environment variables or other methods.
|
||||||
|
|
||||||
|
Say that you have installed a custom tool from the CrewAI Enterprise Tool Repository called "my-tool":
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crewai tool install my-tool
|
||||||
|
```
|
||||||
|
|
||||||
|
And now you want to add another package to your project, you can use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crewai uv add requests
|
||||||
|
```
|
||||||
|
|
||||||
|
Other commands like `uv sync` or `uv remove` can also be used with the `crewai uv` command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crewai uv sync
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crewai uv remove requests
|
||||||
|
```
|
||||||
|
|
||||||
|
This will add the package to your project and update `pyproject.toml` accordingly.
|
||||||
|
|
||||||
## Creating and Publishing Tools
|
## Creating and Publishing Tools
|
||||||
|
|
||||||
To create a new tool project:
|
To create a new tool project:
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
from importlib.metadata import version as get_version
|
from importlib.metadata import version as get_version
|
||||||
|
|
||||||
import click
|
import click
|
||||||
@@ -8,6 +10,7 @@ from crewai.cli.create_crew import create_crew
|
|||||||
from crewai.cli.create_flow import create_flow
|
from crewai.cli.create_flow import create_flow
|
||||||
from crewai.cli.crew_chat import run_chat
|
from crewai.cli.crew_chat import run_chat
|
||||||
from crewai.cli.settings.main import SettingsCommand
|
from crewai.cli.settings.main import SettingsCommand
|
||||||
|
from crewai.cli.utils import build_env_with_tool_repository_credentials, read_toml
|
||||||
from crewai.memory.storage.kickoff_task_outputs_storage import (
|
from crewai.memory.storage.kickoff_task_outputs_storage import (
|
||||||
KickoffTaskOutputsSQLiteStorage,
|
KickoffTaskOutputsSQLiteStorage,
|
||||||
)
|
)
|
||||||
@@ -34,6 +37,46 @@ def crewai():
|
|||||||
"""Top-level command group for crewai."""
|
"""Top-level command group for crewai."""
|
||||||
|
|
||||||
|
|
||||||
|
@crewai.command(
|
||||||
|
name="uv",
|
||||||
|
context_settings=dict(
|
||||||
|
ignore_unknown_options=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@click.argument("uv_args", nargs=-1, type=click.UNPROCESSED)
|
||||||
|
def uv(uv_args):
|
||||||
|
"""A wrapper around uv commands that adds custom tool authentication through env vars."""
|
||||||
|
env = os.environ.copy()
|
||||||
|
try:
|
||||||
|
pyproject_data = read_toml()
|
||||||
|
sources = pyproject_data.get("tool", {}).get("uv", {}).get("sources", {})
|
||||||
|
|
||||||
|
for source_config in sources.values():
|
||||||
|
if isinstance(source_config, dict):
|
||||||
|
index = source_config.get("index")
|
||||||
|
if index:
|
||||||
|
index_env = build_env_with_tool_repository_credentials(index)
|
||||||
|
env.update(index_env)
|
||||||
|
except (FileNotFoundError, KeyError) as e:
|
||||||
|
raise SystemExit(
|
||||||
|
"Error. A valid pyproject.toml file is required. Check that a valid pyproject.toml file exists in the current directory."
|
||||||
|
) from e
|
||||||
|
except Exception as e:
|
||||||
|
raise SystemExit(f"Error: {e}") from e
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.run( # noqa: S603
|
||||||
|
["uv", *uv_args], # noqa: S607
|
||||||
|
capture_output=False,
|
||||||
|
env=env,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
click.secho(f"uv command failed with exit code {e.returncode}", fg="red")
|
||||||
|
raise SystemExit(e.returncode) from e
|
||||||
|
|
||||||
|
|
||||||
@crewai.command()
|
@crewai.command()
|
||||||
@click.argument("type", type=click.Choice(["crew", "flow"]))
|
@click.argument("type", type=click.Choice(["crew", "flow"]))
|
||||||
@click.argument("name")
|
@click.argument("name")
|
||||||
@@ -239,11 +282,6 @@ def deploy():
|
|||||||
"""Deploy the Crew CLI group."""
|
"""Deploy the Crew CLI group."""
|
||||||
|
|
||||||
|
|
||||||
@crewai.group()
|
|
||||||
def tool():
|
|
||||||
"""Tool Repository related commands."""
|
|
||||||
|
|
||||||
|
|
||||||
@deploy.command(name="create")
|
@deploy.command(name="create")
|
||||||
@click.option("-y", "--yes", is_flag=True, help="Skip the confirmation prompt")
|
@click.option("-y", "--yes", is_flag=True, help="Skip the confirmation prompt")
|
||||||
def deploy_create(yes: bool):
|
def deploy_create(yes: bool):
|
||||||
@@ -291,6 +329,11 @@ def deploy_remove(uuid: str | None):
|
|||||||
deploy_cmd.remove_crew(uuid=uuid)
|
deploy_cmd.remove_crew(uuid=uuid)
|
||||||
|
|
||||||
|
|
||||||
|
@crewai.group()
|
||||||
|
def tool():
|
||||||
|
"""Tool Repository related commands."""
|
||||||
|
|
||||||
|
|
||||||
@tool.command(name="create")
|
@tool.command(name="create")
|
||||||
@click.argument("handle")
|
@click.argument("handle")
|
||||||
def tool_create(handle: str):
|
def tool_create(handle: str):
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from crewai.cli import git
|
|||||||
from crewai.cli.command import BaseCommand, PlusAPIMixin
|
from crewai.cli.command import BaseCommand, PlusAPIMixin
|
||||||
from crewai.cli.config import Settings
|
from crewai.cli.config import Settings
|
||||||
from crewai.cli.utils import (
|
from crewai.cli.utils import (
|
||||||
|
build_env_with_tool_repository_credentials,
|
||||||
extract_available_exports,
|
extract_available_exports,
|
||||||
get_project_description,
|
get_project_description,
|
||||||
get_project_name,
|
get_project_name,
|
||||||
@@ -42,8 +43,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
|
|||||||
if project_root.exists():
|
if project_root.exists():
|
||||||
click.secho(f"Folder {folder_name} already exists.", fg="red")
|
click.secho(f"Folder {folder_name} already exists.", fg="red")
|
||||||
raise SystemExit
|
raise SystemExit
|
||||||
else:
|
os.makedirs(project_root)
|
||||||
os.makedirs(project_root)
|
|
||||||
|
|
||||||
click.secho(f"Creating custom tool {folder_name}...", fg="green", bold=True)
|
click.secho(f"Creating custom tool {folder_name}...", fg="green", bold=True)
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
|
|||||||
os.chdir(project_root)
|
os.chdir(project_root)
|
||||||
try:
|
try:
|
||||||
self.login()
|
self.login()
|
||||||
subprocess.run(["git", "init"], check=True)
|
subprocess.run(["git", "init"], check=True) # noqa: S607
|
||||||
console.print(
|
console.print(
|
||||||
f"[green]Created custom tool [bold]{folder_name}[/bold]. Run [bold]cd {project_root}[/bold] to start working.[/green]"
|
f"[green]Created custom tool [bold]{folder_name}[/bold]. Run [bold]cd {project_root}[/bold] to start working.[/green]"
|
||||||
)
|
)
|
||||||
@@ -76,10 +76,10 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
|
|||||||
raise SystemExit()
|
raise SystemExit()
|
||||||
|
|
||||||
project_name = get_project_name(require=True)
|
project_name = get_project_name(require=True)
|
||||||
assert isinstance(project_name, str)
|
assert isinstance(project_name, str) # noqa: S101
|
||||||
|
|
||||||
project_version = get_project_version(require=True)
|
project_version = get_project_version(require=True)
|
||||||
assert isinstance(project_version, str)
|
assert isinstance(project_version, str) # noqa: S101
|
||||||
|
|
||||||
project_description = get_project_description(require=False)
|
project_description = get_project_description(require=False)
|
||||||
encoded_tarball = None
|
encoded_tarball = None
|
||||||
@@ -94,8 +94,8 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
|
|||||||
self._print_current_organization()
|
self._print_current_organization()
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as temp_build_dir:
|
with tempfile.TemporaryDirectory() as temp_build_dir:
|
||||||
subprocess.run(
|
subprocess.run( # noqa: S603
|
||||||
["uv", "build", "--sdist", "--out-dir", temp_build_dir],
|
["uv", "build", "--sdist", "--out-dir", temp_build_dir], # noqa: S607
|
||||||
check=True,
|
check=True,
|
||||||
capture_output=False,
|
capture_output=False,
|
||||||
)
|
)
|
||||||
@@ -146,7 +146,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
|
|||||||
style="bold red",
|
style="bold red",
|
||||||
)
|
)
|
||||||
raise SystemExit
|
raise SystemExit
|
||||||
elif get_response.status_code != 200:
|
if get_response.status_code != 200:
|
||||||
console.print(
|
console.print(
|
||||||
"Failed to get tool details. Please try again later.", style="bold red"
|
"Failed to get tool details. Please try again later.", style="bold red"
|
||||||
)
|
)
|
||||||
@@ -196,10 +196,10 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
|
|||||||
else:
|
else:
|
||||||
add_package_command.extend(["--index", index, tool_handle])
|
add_package_command.extend(["--index", index, tool_handle])
|
||||||
|
|
||||||
add_package_result = subprocess.run(
|
add_package_result = subprocess.run( # noqa: S603
|
||||||
add_package_command,
|
add_package_command,
|
||||||
capture_output=False,
|
capture_output=False,
|
||||||
env=self._build_env_with_credentials(repository_handle),
|
env=build_env_with_tool_repository_credentials(repository_handle),
|
||||||
text=True,
|
text=True,
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
@@ -221,20 +221,6 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
|
|||||||
)
|
)
|
||||||
raise SystemExit
|
raise SystemExit
|
||||||
|
|
||||||
def _build_env_with_credentials(self, repository_handle: str):
|
|
||||||
repository_handle = repository_handle.upper().replace("-", "_")
|
|
||||||
settings = Settings()
|
|
||||||
|
|
||||||
env = os.environ.copy()
|
|
||||||
env[f"UV_INDEX_{repository_handle}_USERNAME"] = str(
|
|
||||||
settings.tool_repository_username or ""
|
|
||||||
)
|
|
||||||
env[f"UV_INDEX_{repository_handle}_PASSWORD"] = str(
|
|
||||||
settings.tool_repository_password or ""
|
|
||||||
)
|
|
||||||
|
|
||||||
return env
|
|
||||||
|
|
||||||
def _print_current_organization(self) -> None:
|
def _print_current_organization(self) -> None:
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
if settings.org_uuid:
|
if settings.org_uuid:
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import click
|
|||||||
import tomli
|
import tomli
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
||||||
|
from crewai.cli.config import Settings
|
||||||
from crewai.cli.constants import ENV_VARS
|
from crewai.cli.constants import ENV_VARS
|
||||||
from crewai.crew import Crew
|
from crewai.crew import Crew
|
||||||
from crewai.flow import Flow
|
from crewai.flow import Flow
|
||||||
@@ -417,6 +418,21 @@ def extract_available_exports(dir_path: str = "src"):
|
|||||||
raise SystemExit(1) from e
|
raise SystemExit(1) from e
|
||||||
|
|
||||||
|
|
||||||
|
def build_env_with_tool_repository_credentials(repository_handle: str):
|
||||||
|
repository_handle = repository_handle.upper().replace("-", "_")
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
env[f"UV_INDEX_{repository_handle}_USERNAME"] = str(
|
||||||
|
settings.tool_repository_username or ""
|
||||||
|
)
|
||||||
|
env[f"UV_INDEX_{repository_handle}_PASSWORD"] = str(
|
||||||
|
settings.tool_repository_password or ""
|
||||||
|
)
|
||||||
|
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
def _load_tools_from_init(init_file: Path) -> list[dict[str, Any]]:
|
def _load_tools_from_init(init_file: Path) -> list[dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Load and validate tools from a given __init__.py file.
|
Load and validate tools from a given __init__.py file.
|
||||||
|
|||||||
Reference in New Issue
Block a user