Feat/poetry to uv migration (#1406)

* feat: Start migrating to UV

* feat: add uv to flows

* feat: update docs on Poetry -> uv

* feat: update docs and uv.locl

* feat: update tests and github CI

* feat: run ruff format

* feat: update typechecking

* feat: fix type checking

* feat: update python version

* feat: type checking gic

* feat: adapt uv command to run the tool repo

* Adapt tool build command to uv

* feat: update logic to let only projects with crew to be deployed

* feat: add uv to tools

* fix; tests

* fix: remove breakpoint

* fix :test

* feat: add crewai update to migrate from poetry to uv

* fix: tests

* feat: add validation for ˆ character on pyproject

* feat: add run_crew to pyproject if doesnt exist

* feat: add validation for poetry migration

* fix: warning

---------

Co-authored-by: Vinicius Brasil <vini@hey.com>
This commit is contained in:
Eduardo Chiarotti
2024-10-11 19:11:27 -03:00
committed by GitHub
parent 6534a909d6
commit 7f387dd7c3
33 changed files with 5753 additions and 314 deletions

View File

@@ -9,24 +9,24 @@ env:
OPENAI_API_KEY: fake-api-key
jobs:
deploy:
tests:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
python-version: "3.11.9"
enable-cache: true
- name: Install Requirements
run: |
set -e
pip install poetry
poetry install
- name: Set up Python
run: uv python install 3.11.9
- name: Install the project
run: uv sync --dev
- name: Run tests
run: poetry run pytest
run: uv run pytest tests

View File

@@ -16,7 +16,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: "3.10"
python-version: "3.11.9"
- name: Install Requirements
run: |

View File

@@ -44,15 +44,9 @@ To get started with CrewAI, follow these simple steps:
### 1. Installation
Ensure you have Python >=3.10 <=3.13 installed on your system. CrewAI uses [Poetry](https://python-poetry.org/) for dependency management and package handling, offering a seamless setup and execution experience.
Ensure you have Python >=3.10 <=3.13 installed on your system. CrewAI uses [UV](https://docs.astral.sh/uv/) for dependency management and package handling, offering a seamless setup and execution experience.
First, if you haven't already, install Poetry:
```bash
pip install poetry
```
Then, install CrewAI:
First, install CrewAI:
```shell
pip install crewai
@@ -243,7 +237,7 @@ Lock the dependencies and install them by using the CLI command but first, navig
```shell
cd my_project
crewai install
crewai install (Optional)
```
To run your crew, execute the following command in the root of your project:
@@ -332,14 +326,14 @@ CrewAI is open-source and we welcome contributions. If you're looking to contrib
### Installing Dependencies
```bash
poetry lock
poetry install
uv lock
uv sync
```
### Virtual Env
```bash
poetry shell
uv venv
```
### Pre-commit hooks
@@ -351,19 +345,19 @@ pre-commit install
### Running Tests
```bash
poetry run pytest
uvx pytest
```
### Running static type checks
```bash
poetry run mypy
uvx mypy
```
### Packaging
```bash
poetry build
uv build
```
### Installing Locally

View File

@@ -10,10 +10,10 @@ The CrewAI CLI provides a set of commands to interact with CrewAI, allowing you
## Installation
To use the CrewAI CLI, make sure you have CrewAI & Poetry installed:
To use the CrewAI CLI, make sure you have CrewAI installed:
```shell
pip install crewai poetry
pip install crewai
```
## Basic Usage
@@ -145,4 +145,4 @@ crewai run
<Note>
Make sure to run these commands from the directory where your CrewAI project is set up.
Some commands may require additional configuration or setup within your project structure.
</Note>
</Note>

View File

@@ -572,16 +572,16 @@ In this example, the `PoemFlow` class defines a flow that generates a sentence c
### Running the Flow
Before running the flow, make sure to install the dependencies by running:
(Optional) Before running the flow, you can install the dependencies by running:
```bash
poetry install
crewai install
```
Once all of the dependencies are installed, you need to activate the virtual environment by running:
```bash
poetry shell
source .venv/bin/activate
```
After activating the virtual environment, you can run the flow by executing one of the following commands:
@@ -593,7 +593,7 @@ crewai flow run
or
```bash
poetry run run_flow
uv run run_flow
```
The flow will execute, and you should see the output in the console.

View File

@@ -0,0 +1,163 @@
# Creating a CrewAI Pipeline Project
Welcome to the comprehensive guide for creating a new CrewAI pipeline project. This document will walk you through the steps to create, customize, and run your CrewAI pipeline project, ensuring you have everything you need to get started.
To learn more about CrewAI pipelines, visit the [CrewAI documentation](https://docs.crewai.com/core-concepts/Pipeline/).
## Prerequisites
Before getting started with CrewAI pipelines, make sure that you have installed CrewAI via pip:
```shell
$ pip install crewai crewai-tools
```
The same prerequisites for virtual environments and Code IDEs apply as in regular CrewAI projects.
## Creating a New Pipeline Project
To create a new CrewAI pipeline project, you have two options:
1. For a basic pipeline template:
```shell
$ crewai create pipeline <project_name>
```
2. For a pipeline example that includes a router:
```shell
$ crewai create pipeline --router <project_name>
```
These commands will create a new project folder with the following structure:
```
<project_name>/
├── README.md
├── uv.lock
├── pyproject.toml
├── src/
│ └── <project_name>/
│ ├── __init__.py
│ ├── main.py
│ ├── crews/
│ │ ├── crew1/
│ │ │ ├── crew1.py
│ │ │ └── config/
│ │ │ ├── agents.yaml
│ │ │ └── tasks.yaml
│ │ ├── crew2/
│ │ │ ├── crew2.py
│ │ │ └── config/
│ │ │ ├── agents.yaml
│ │ │ └── tasks.yaml
│ ├── pipelines/
│ │ ├── __init__.py
│ │ ├── pipeline1.py
│ │ └── pipeline2.py
│ └── tools/
│ ├── __init__.py
│ └── custom_tool.py
└── tests/
```
## Customizing Your Pipeline Project
To customize your pipeline project, you can:
1. Modify the crew files in `src/<project_name>/crews/` to define your agents and tasks for each crew.
2. Modify the pipeline files in `src/<project_name>/pipelines/` to define your pipeline structure.
3. Modify `src/<project_name>/main.py` to set up and run your pipelines.
4. Add your environment variables into the `.env` file.
## Example 1: Defining a Two-Stage Sequential Pipeline
Here's an example of how to define a pipeline with sequential stages in `src/<project_name>/pipelines/pipeline.py`:
```python
from crewai import Pipeline
from crewai.project import PipelineBase
from ..crews.research_crew.research_crew import ResearchCrew
from ..crews.write_x_crew.write_x_crew import WriteXCrew
@PipelineBase
class SequentialPipeline:
def __init__(self):
# Initialize crews
self.research_crew = ResearchCrew().crew()
self.write_x_crew = WriteXCrew().crew()
def create_pipeline(self):
return Pipeline(
stages=[
self.research_crew,
self.write_x_crew
]
)
async def kickoff(self, inputs):
pipeline = self.create_pipeline()
results = await pipeline.kickoff(inputs)
return results
```
## Example 2: Defining a Two-Stage Pipeline with Parallel Execution
```python
from crewai import Pipeline
from crewai.project import PipelineBase
from ..crews.research_crew.research_crew import ResearchCrew
from ..crews.write_x_crew.write_x_crew import WriteXCrew
from ..crews.write_linkedin_crew.write_linkedin_crew import WriteLinkedInCrew
@PipelineBase
class ParallelExecutionPipeline:
def __init__(self):
# Initialize crews
self.research_crew = ResearchCrew().crew()
self.write_x_crew = WriteXCrew().crew()
self.write_linkedin_crew = WriteLinkedInCrew().crew()
def create_pipeline(self):
return Pipeline(
stages=[
self.research_crew,
[self.write_x_crew, self.write_linkedin_crew] # Parallel execution
]
)
async def kickoff(self, inputs):
pipeline = self.create_pipeline()
results = await pipeline.kickoff(inputs)
return results
```
### Annotations
The main annotation you'll use for pipelines is `@PipelineBase`. This annotation is used to decorate your pipeline classes, similar to how `@CrewBase` is used for crews.
## Installing Dependencies
To install the dependencies for your project, use `uv` the install command is optional because when running `crewai run`, it will automatically install the dependencies for you:
```shell
$ cd <project_name>
$ crewai install (optional)
```
## Running Your Pipeline Project
To run your pipeline project, use the following command:
```shell
$ crewai run
```
This will initialize your pipeline and begin task execution as defined in your `main.py` file.
## Deploying Your Pipeline Project
Pipelines can be deployed in the same way as regular CrewAI projects. The easiest way is through [CrewAI+](https://www.crewai.com/crewaiplus), where you can deploy your pipeline in a few clicks.
Remember, when working with pipelines, you're orchestrating multiple crews to work together in a sequence or parallel fashion. This allows for more complex workflows and information processing tasks.

View File

@@ -0,0 +1,236 @@
---
title: Starting a New CrewAI Project - Using Template
description: A comprehensive guide to starting a new CrewAI project, including the latest updates and project setup methods.
---
# Starting Your CrewAI Project
Welcome to the ultimate guide for starting a new CrewAI project. This document will walk you through the steps to create, customize, and run your CrewAI project, ensuring you have everything you need to get started.
Before we start, there are a couple of things to note:
1. CrewAI is a Python package and requires Python >=3.10 and <=3.13 to run.
2. The preferred way of setting up CrewAI is using the `crewai create crew` command. This will create a new project folder and install a skeleton template for you to work on.
## Prerequisites
Before getting started with CrewAI, make sure that you have installed it via pip:
```shell
$ pip install 'crewai[tools]'
```
## Creating a New Project
In this example, we will be using `uv` as our virtual environment manager.
To create a new CrewAI project, run the following CLI command:
```shell
$ crewai create crew <project_name>
```
This command will create a new project folder with the following structure:
```shell
my_project/
├── .gitignore
├── pyproject.toml
├── README.md
└── src/
└── my_project/
├── __init__.py
├── main.py
├── crew.py
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml
└── tasks.yaml
```
You can now start developing your project by editing the files in the `src/my_project` folder. The `main.py` file is the entry point of your project, and the `crew.py` file is where you define your agents and tasks.
## Customizing Your Project
To customize your project, you can:
- Modify `src/my_project/config/agents.yaml` to define your agents.
- Modify `src/my_project/config/tasks.yaml` to define your tasks.
- Modify `src/my_project/crew.py` to add your own logic, tools, and specific arguments.
- Modify `src/my_project/main.py` to add custom inputs for your agents and tasks.
- Add your environment variables into the `.env` file.
### Example: Defining Agents and Tasks
#### agents.yaml
```yaml
researcher:
role: >
Job Candidate Researcher
goal: >
Find potential candidates for the job
backstory: >
You are adept at finding the right candidates by exploring various online
resources. Your skill in identifying suitable candidates ensures the best
match for job positions.
```
#### tasks.yaml
```yaml
research_candidates_task:
description: >
Conduct thorough research to find potential candidates for the specified job.
Utilize various online resources and databases to gather a comprehensive list of potential candidates.
Ensure that the candidates meet the job requirements provided.
Job Requirements:
{job_requirements}
expected_output: >
A list of 10 potential candidates with their contact information and brief profiles highlighting their suitability.
agent: researcher # THIS NEEDS TO MATCH THE AGENT NAME IN THE AGENTS.YAML FILE AND THE AGENT DEFINED IN THE crew.py FILE
context: # THESE NEED TO MATCH THE TASK NAMES DEFINED ABOVE AND THE TASKS.YAML FILE AND THE TASK DEFINED IN THE crew.py FILE
- researcher
```
### Referencing Variables:
Your defined functions with the same name will be used. For example, you can reference the agent for specific tasks from `tasks.yaml` file. Ensure your annotated agent and function name are the same; otherwise, your task won't recognize the reference properly.
#### Example References
`agents.yaml`
```yaml
email_summarizer:
role: >
Email Summarizer
goal: >
Summarize emails into a concise and clear summary
backstory: >
You will create a 5 bullet point summary of the report
llm: mixtal_llm
```
`tasks.yaml`
```yaml
email_summarizer_task:
description: >
Summarize the email into a 5 bullet point summary
expected_output: >
A 5 bullet point summary of the email
agent: email_summarizer
context:
- reporting_task
- research_task
```
Use the annotations to properly reference the agent and task in the `crew.py` file.
### Annotations include:
* `@agent`
* `@task`
* `@crew`
* `@tool`
* `@callback`
* `@output_json`
* `@output_pydantic`
* `@cache_handler`
`crew.py`
```python
# ...
@agent
def email_summarizer(self) -> Agent:
return Agent(
config=self.agents_config["email_summarizer"],
)
@task
def email_summarizer_task(self) -> Task:
return Task(
config=self.tasks_config["email_summarizer_task"],
)
# ...
```
## Installing Dependencies
To install the dependencies for your project, you can use `uv`. Running the following command is optional since when running `crewai run`, it will automatically install the dependencies for you.
```shell
$ cd my_project
$ crewai install (optional)
```
This will install the dependencies specified in the `pyproject.toml` file.
## Interpolating Variables
Any variable interpolated in your `agents.yaml` and `tasks.yaml` files like `{variable}` will be replaced by the value of the variable in the `main.py` file.
#### tasks.yaml
```yaml
research_task:
description: >
Conduct a thorough research about the customer and competitors in the context
of {customer_domain}.
Make sure you find any interesting and relevant information given the
current year is 2024.
expected_output: >
A complete report on the customer and their customers and competitors,
including their demographics, preferences, market positioning and audience engagement.
```
#### main.py
```python
# main.py
def run():
inputs = {
"customer_domain": "crewai.com"
}
MyProjectCrew(inputs).crew().kickoff(inputs=inputs)
```
## Running Your Project
To run your project, use the following command:
```shell
$ crewai run
```
This will initialize your crew of AI agents and begin task execution as defined in your configuration in the `main.py` file.
### Replay Tasks from Latest Crew Kickoff
CrewAI now includes a replay feature that allows you to list the tasks from the last run and replay from a specific one. To use this feature, run:
```shell
$ crewai replay <task_id>
```
Replace `<task_id>` with the ID of the task you want to replay.
### Reset Crew Memory
If you need to reset the memory of your crew before running it again, you can do so by calling the reset memory feature:
```shell
$ crewai reset-memory
```
This will clear the crew's memory, allowing for a fresh start.
## Deploying Your Project
The easiest way to deploy your crew is through [CrewAI+](https://www.crewai.com/crewaiplus), where you can deploy your crew in a few clicks.

View File

@@ -1,65 +1,66 @@
[tool.poetry]
[project]
name = "crewai"
version = "0.70.1"
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>"]
readme = "README.md"
packages = [{ include = "crewai", from = "src" }]
requires-python = ">=3.10,<=3.13"
authors = [
{ name = "Joao Moura", email = "joao@crewai.com" }
]
dependencies = [
"pydantic>=2.4.2",
"langchain>=0.2.16",
"openai>=1.13.3",
"opentelemetry-api>=1.22.0",
"opentelemetry-sdk>=1.22.0",
"opentelemetry-exporter-otlp-proto-http>=1.22.0",
"instructor>=1.3.3",
"regex>=2024.9.11",
"crewai-tools>=0.12.1",
"click>=8.1.7",
"python-dotenv>=1.0.0",
"appdirs>=1.4.4",
"jsonref>=1.1.0",
"agentops>=0.3.0",
"embedchain>=0.1.114",
"json-repair>=0.25.2",
"auth0-python>=4.7.1",
"litellm>=1.44.22",
"pyvis>=0.3.2",
"uv>=0.4.18",
"tomli-w>=1.1.0",
]
[tool.poetry.urls]
[project.urls]
Homepage = "https://crewai.com"
Documentation = "https://docs.crewai.com"
Repository = "https://github.com/crewAIInc/crewAI"
[tool.poetry.dependencies]
python = ">=3.10,<=3.13"
pydantic = "^2.4.2"
langchain = "^0.2.16"
openai = "^1.13.3"
opentelemetry-api = "^1.22.0"
opentelemetry-sdk = "^1.22.0"
opentelemetry-exporter-otlp-proto-http = "^1.22.0"
instructor = "1.3.3"
regex = "^2024.9.11"
crewai-tools = { version = "^0.12.1", optional = true }
click = "^8.1.7"
python-dotenv = "^1.0.0"
appdirs = "^1.4.4"
jsonref = "^1.1.0"
agentops = { version = "^0.3.0", optional = true }
embedchain = "^0.1.114"
json-repair = "^0.25.2"
auth0-python = "^4.7.1"
poetry = "^1.8.3"
litellm = "^1.44.22"
pyvis = "^0.3.2"
[project.optional-dependencies]
tools = ["crewai-tools>=0.12.1"]
agentops = ["agentops>=0.3.0"]
[tool.poetry.extras]
tools = ["crewai-tools"]
agentops = ["agentops"]
[tool.uv]
dev-dependencies = [
"ruff>=0.4.10",
"mypy>=1.10.0",
"pre-commit>=3.6.0",
"mkdocs>=1.4.3",
"mkdocstrings>=0.22.0",
"mkdocstrings-python>=1.1.2",
"mkdocs-material>=9.5.7",
"mkdocs-material-extensions>=1.3.1",
"pillow>=10.2.0",
"cairosvg>=2.7.1",
"crewai-tools>=0.12.1",
"pytest>=8.0.0",
"pytest-vcr>=1.0.2",
"python-dotenv>=1.0.0",
"pytest-asyncio>=0.23.7",
"pytest-subprocess>=1.5.2",
]
[tool.poetry.group.dev.dependencies]
isort = "^5.13.2"
mypy = "1.10.0"
autoflake = "^2.2.1"
pre-commit = "^3.6.0"
mkdocs = "^1.4.3"
mkdocstrings = "^0.22.0"
mkdocstrings-python = "^1.1.2"
mkdocs-material = { extras = ["imaging"], version = "^9.5.7" }
mkdocs-material-extensions = "^1.3.1"
pillow = "^10.2.0"
cairosvg = "^2.7.1"
crewai-tools = "^0.12.1"
[tool.poetry.group.test.dependencies]
pytest = "^8.0.0"
pytest-vcr = "^1.0.2"
python-dotenv = "1.0.0"
pytest-asyncio = "^0.23.7"
pytest-subprocess = "^1.5.2"
[tool.poetry.scripts]
[project.scripts]
crewai = "crewai.cli.cli:crewai"
[tool.mypy]
@@ -71,5 +72,5 @@ exclude = ["cli/templates"]
exclude_dirs = ["src/crewai/cli/templates"]
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = ["hatchling"]
build-backend = "hatchling.build"

View File

@@ -317,9 +317,9 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
if self.crew is not None and hasattr(self.crew, "_train_iteration"):
train_iteration = self.crew._train_iteration
if agent_id in training_data and isinstance(train_iteration, int):
training_data[agent_id][train_iteration][
"improved_output"
] = result.output
training_data[agent_id][train_iteration]["improved_output"] = (
result.output
)
training_handler.save(training_data)
else:
self._logger.log(

View File

@@ -21,6 +21,7 @@ from .run_crew import run_crew
from .run_flow import run_flow
from .tools.main import ToolCommand
from .train_crew import train_crew
from .update_crew import update_crew
@click.group()
@@ -188,6 +189,12 @@ def run():
run_crew()
@crewai.command()
def update():
"""Update the pyproject.toml of the Crew project to use uv."""
update_crew()
@crewai.command()
def signup():
"""Sign Up/Login to CrewAI+."""
@@ -276,7 +283,13 @@ def tool_install(handle: str):
@tool.command(name="publish")
@click.option("--force", is_flag=True, show_default=True, default=False, help="Bypasses Git remote validations")
@click.option(
"--force",
is_flag=True,
show_default=True,
default=False,
help="Bypasses Git remote validations",
)
@click.option("--public", "is_public", flag_value=True, default=False)
@click.option("--private", "is_public", flag_value=False)
def tool_publish(is_public: bool, force: bool):

View File

@@ -5,13 +5,13 @@ import click
def evaluate_crew(n_iterations: int, model: str) -> None:
"""
Test and Evaluate the crew by running a command in the Poetry environment.
Test and Evaluate the crew by running a command in the UV environment.
Args:
n_iterations (int): The number of iterations to test the crew.
model (str): The model to test the crew with.
"""
command = ["poetry", "run", "test", str(n_iterations), model]
command = ["uv", "run", "test", str(n_iterations), model]
try:
if n_iterations <= 0:

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import subprocess
import click
@@ -9,7 +10,7 @@ def replay_task_command(task_id: str) -> None:
Args:
task_id (str): The ID of the task to replay from.
"""
command = ["poetry", "run", "replay", task_id]
command = ["uv", "run", "replay", task_id]
try:
result = subprocess.run(command, capture_output=False, text=True, check=True)

View File

@@ -5,19 +5,21 @@ import click
def run_crew() -> None:
"""
Run the crew by running a command in the Poetry environment.
Run the crew by running a command in the UV environment.
"""
command = ["poetry", "run", "run_crew"]
command = ["uv", "run", "run_crew"]
try:
result = subprocess.run(command, capture_output=False, text=True, check=True)
if result.stderr:
click.echo(result.stderr, err=True)
subprocess.run(command, capture_output=True, text=True, check=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)
click.echo(e.output, err=True, nl=True)
click.echo(e.stderr, err=True, nl=True)
if "table found" in e.stderr:
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.",
fg="yellow",
)
except Exception as e:
click.echo(f"An unexpected error occurred: {e}", err=True)

View File

@@ -5,9 +5,9 @@ import click
def run_flow() -> None:
"""
Run the flow by running a command in the Poetry environment.
Run the flow by running a command in the UV environment.
"""
command = ["poetry", "run", "run_flow"]
command = ["uv", "run", "run_flow"]
try:
result = subprocess.run(command, capture_output=False, text=True, check=True)

View File

@@ -4,17 +4,17 @@ Welcome to the {{crew_name}} Crew project, powered by [crewAI](https://crewai.co
## Installation
Ensure you have Python >=3.10 <=3.13 installed on your system. This project uses [Poetry](https://python-poetry.org/) for dependency management and package handling, offering a seamless setup and execution experience.
Ensure you have Python >=3.10 <=3.13 installed on your system. This project uses [UV](https://docs.astral.sh/uv/) for dependency management and package handling, offering a seamless setup and execution experience.
First, if you haven't already, install Poetry:
First, if you haven't already, install uv:
```bash
pip install poetry
pip install uv
```
Next, navigate to your project directory and install the dependencies:
1. First lock the dependencies and install them by using the CLI command:
(Optional) Lock the dependencies and install them by using the CLI command:
```bash
crewai install
```

View File

@@ -1,15 +1,14 @@
[tool.poetry]
[project]
name = "{{folder_name}}"
version = "0.1.0"
description = "{{name}} using crewAI"
authors = ["Your Name <you@example.com>"]
authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<=3.13"
dependencies = [
"crewai[tools]>=0.67.1,<1.0.0"
]
[tool.poetry.dependencies]
python = ">=3.10,<=3.13"
crewai = { extras = ["tools"], version = ">=0.70.1,<1.0.0" }
[tool.poetry.scripts]
[project.scripts]
{{folder_name}} = "{{folder_name}}.main:run"
run_crew = "{{folder_name}}.main:run"
train = "{{folder_name}}.main:train"
@@ -17,5 +16,5 @@ replay = "{{folder_name}}.main:replay"
test = "{{folder_name}}.main:test"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = ["hatchling"]
build-backend = "hatchling.build"

View File

@@ -4,18 +4,17 @@ Welcome to the {{crew_name}} Crew project, powered by [crewAI](https://crewai.co
## Installation
Ensure you have Python >=3.10 <=3.13 installed on your system. This project uses [Poetry](https://python-poetry.org/) for dependency management and package handling, offering a seamless setup and execution experience.
Ensure you have Python >=3.10 <=3.13 installed on your system. This project uses [UV](https://docs.astral.sh/uv/) for dependency management and package handling, offering a seamless setup and execution experience.
First, if you haven't already, install Poetry:
First, if you haven't already, install uv:
```bash
pip install poetry
pip install uv
```
Next, navigate to your project directory and install the dependencies:
1. First lock the dependencies and then install them:
(Optional) Lock the dependencies and install them by using the CLI command:
```bash
crewai install
```

View File

@@ -1,19 +1,19 @@
[tool.poetry]
[project]
name = "{{folder_name}}"
version = "0.1.0"
description = "{{name}} using crewAI"
authors = ["Your Name <you@example.com>"]
authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<=3.13"
dependencies = [
"crewai[tools]>=0.67.1,<1.0.0",
"asyncio"
]
[tool.poetry.dependencies]
python = ">=3.10,<=3.13"
crewai = { extras = ["tools"], version = ">=0.70.1,<1.0.0" }
asyncio = "*"
[tool.poetry.scripts]
[project.scripts]
{{folder_name}} = "{{folder_name}}.main:main"
run_flow = "{{folder_name}}.main:main"
plot_flow = "{{folder_name}}.main:plot"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = ["hatchling"]
build-backend = "hatchling.build"

View File

@@ -1,20 +1,21 @@
[tool.poetry]
[project]
name = "{{folder_name}}"
version = "0.1.0"
description = "{{name}} using crewAI"
authors = ["Your Name <you@example.com>"]
requires-python = ">=3.10,<=3.13"
dependencies = [
"crewai[tools]>=0.67.1,<1.0.0"
]
[tool.poetry.dependencies]
python = ">=3.10,<=3.13"
crewai = { extras = ["tools"], version = ">=0.70.1,<1.0.0" }
[tool.poetry.scripts]
[project.scripts]
{{folder_name}} = "{{folder_name}}.main:main"
run_crew = "{{folder_name}}.main:main"
train = "{{folder_name}}.main:train"
replay = "{{folder_name}}.main:replay"
test = "{{folder_name}}.main:test"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = ["hatchling"]
build-backend = "hatchling.build"

View File

@@ -6,13 +6,13 @@ custom tools to power up your crews.
## Installing
Ensure you have Python >=3.10 <=3.13 installed on your system. This project
uses [Poetry](https://python-poetry.org/) for dependency management and package
uses [UV](https://docs.astral.sh/uv/) for dependency management and package
handling, offering a seamless setup and execution experience.
First, if you haven't already, install Poetry:
First, if you haven't already, install `uv`:
```bash
pip install poetry
pip install uv
```
Next, navigate to your project directory and install the dependencies with:

View File

@@ -1,20 +1,22 @@
import base64
from pathlib import Path
import click
import os
import platform
import subprocess
import tempfile
from pathlib import Path
import click
from rich.console import Console
from crewai.cli.command import BaseCommand, PlusAPIMixin
from crewai.cli import git
from crewai.cli.command import BaseCommand, PlusAPIMixin
from crewai.cli.utils import (
get_project_name,
get_project_description,
get_project_name,
get_project_version,
tree_copy,
tree_find_and_replace,
)
from rich.console import Console
console = Console()
@@ -24,6 +26,8 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
A class to handle tool repository related operations for CrewAI projects.
"""
BASE_URL = "https://app.crewai.com/pypi/"
def __init__(self):
BaseCommand.__init__(self)
PlusAPIMixin.__init__(self, telemetry=self._telemetry)
@@ -82,7 +86,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
with tempfile.TemporaryDirectory() as temp_build_dir:
subprocess.run(
["poetry", "build", "-f", "sdist", "--output", temp_build_dir],
["uv", "build", "--sdist", "--out-dir", temp_build_dir],
check=True,
capture_output=False,
)
@@ -92,7 +96,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
)
if not tarball_filename:
console.print(
"Project build failed. Please ensure that the command `poetry build -f sdist` completes successfully.",
"Project build failed. Please ensure that the command `uv build --sdist` completes successfully.",
style="bold red",
)
raise SystemExit
@@ -149,62 +153,39 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
raise SystemExit
login_response_json = login_response.json()
for repository in login_response_json["repositories"]:
self._add_repository_to_poetry(
repository, login_response_json["credential"]
)
self._set_netrc_credentials(login_response_json["credential"])
console.print(
"Succesfully authenticated to the tool repository.", style="bold green"
"Successfully authenticated to the tool repository.", style="bold green"
)
def _add_repository_to_poetry(self, repository, credentials):
repository_handle = f"crewai-{repository['handle']}"
def _set_netrc_credentials(self, credentials):
# Create .netrc or _netrc file
netrc_filename = "_netrc" if platform.system() == "Windows" else ".netrc"
netrc_path = Path.home() / netrc_filename
add_repository_command = [
"poetry",
"source",
"add",
"--priority=explicit",
repository_handle,
repository["url"],
]
add_repository_result = subprocess.run(
add_repository_command, text=True, check=True
)
netrc_content = f"""machine app.crewai.com
login {credentials['username']}
password {credentials['password']}
"""
if add_repository_result.stderr:
click.echo(add_repository_result.stderr, err=True)
raise SystemExit
with open(netrc_path, "a") as netrc_file:
netrc_file.write(netrc_content)
add_repository_credentials_command = [
"poetry",
"config",
f"http-basic.{repository_handle}",
credentials["username"],
credentials["password"],
]
add_repository_credentials_result = subprocess.run(
add_repository_credentials_command,
capture_output=False,
text=True,
check=True,
)
if add_repository_credentials_result.stderr:
click.echo(add_repository_credentials_result.stderr, err=True)
raise SystemExit
# Set appropriate permissions for Unix-like systems
if platform.system() != "Windows":
os.chmod(netrc_path, 0o600)
console.print(f"Added credentials to {netrc_filename}", style="bold green")
def _add_package(self, tool_details):
tool_handle = tool_details["handle"]
repository_handle = tool_details["repository"]["handle"]
pypi_index_handle = f"crewai-{repository_handle}"
add_package_command = [
"poetry",
"uv",
"add",
"--source",
pypi_index_handle,
"--extra-index-url",
self.BASE_URL + repository_handle,
tool_handle,
]
add_package_result = subprocess.run(

View File

@@ -5,12 +5,12 @@ import click
def train_crew(n_iterations: int, filename: str) -> None:
"""
Train the crew by running a command in the Poetry environment.
Train the crew by running a command in the UV environment.
Args:
n_iterations (int): The number of iterations to train the crew.
"""
command = ["poetry", "run", "train", str(n_iterations), filename]
command = ["uv", "run", "train", str(n_iterations), filename]
try:
if n_iterations <= 0:

View File

@@ -0,0 +1,115 @@
import shutil
import tomli_w
import tomllib
def update_crew() -> None:
"""Update the pyproject.toml of the Crew project to use uv."""
migrate_pyproject("pyproject.toml", "pyproject.toml")
def migrate_pyproject(input_file, output_file):
"""
Migrate the pyproject.toml to the new format.
This function is used to migrate the pyproject.toml to the new format.
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.
"""
# Read the input pyproject.toml
with open(input_file, "rb") as f:
pyproject = tomllib.load(f)
# Initialize the new project structure
new_pyproject = {
"project": {},
"build-system": {"requires": ["hatchling"], "build-backend": "hatchling.build"},
}
# Migrate project metadata
if "tool" in pyproject and "poetry" in pyproject["tool"]:
poetry = pyproject["tool"]["poetry"]
new_pyproject["project"]["name"] = poetry.get("name")
new_pyproject["project"]["version"] = poetry.get("version")
new_pyproject["project"]["description"] = poetry.get("description")
new_pyproject["project"]["authors"] = [
{
"name": author.split("<")[0].strip(),
"email": author.split("<")[1].strip(">").strip(),
}
for author in poetry.get("authors", [])
]
new_pyproject["project"]["requires-python"] = poetry.get("python")
else:
# If it's already in the new format, just copy the project section
new_pyproject["project"] = pyproject.get("project", {})
# Migrate or copy dependencies
if "dependencies" in new_pyproject["project"]:
# If dependencies are already in the new format, keep them as is
pass
elif "dependencies" in poetry:
new_pyproject["project"]["dependencies"] = []
for dep, version in poetry["dependencies"].items():
if isinstance(version, dict): # Handle extras
extras = ",".join(version.get("extras", []))
new_dep = f"{dep}[{extras}]"
if "version" in version:
new_dep += parse_version(version["version"])
elif dep == "python":
new_pyproject["project"]["requires-python"] = version
continue
else:
new_dep = f"{dep}{parse_version(version)}"
new_pyproject["project"]["dependencies"].append(new_dep)
# Migrate or copy scripts
if "scripts" in poetry:
new_pyproject["project"]["scripts"] = poetry["scripts"]
elif "scripts" in pyproject.get("project", {}):
new_pyproject["project"]["scripts"] = pyproject["project"]["scripts"]
else:
new_pyproject["project"]["scripts"] = {}
if (
"run_crew" not in new_pyproject["project"]["scripts"]
and len(new_pyproject["project"]["scripts"]) > 0
):
# Extract the module name from any existing script
existing_scripts = new_pyproject["project"]["scripts"]
module_name = next(
(value.split(".")[0] for value in existing_scripts.values() if "." in value)
)
new_pyproject["project"]["scripts"]["run_crew"] = f"{module_name}.main:run"
# Migrate optional dependencies
if "extras" in poetry:
new_pyproject["project"]["optional-dependencies"] = poetry["extras"]
# Backup the old pyproject.toml
backup_file = "pyproject-old.toml"
shutil.copy2(input_file, backup_file)
print(f"Original pyproject.toml backed up as {backup_file}")
# Write the new pyproject.toml
with open(output_file, "wb") as f:
tomli_w.dump(new_pyproject, f)
print(f"Migration complete. New pyproject.toml written to {output_file}")
def parse_version(version: str) -> str:
"""Parse and convert version specifiers."""
if version.startswith("^"):
main_lib_version = version[1:].split(",")[0]
addtional_lib_version = None
if len(version[1:].split(",")) > 1:
addtional_lib_version = version[1:].split(",")[1]
return f">={main_lib_version}" + (
f",{addtional_lib_version}" if addtional_lib_version else ""
)
return version

View File

@@ -1,13 +1,14 @@
import importlib.metadata
import os
import shutil
import click
import sys
import importlib.metadata
from functools import reduce
from typing import Any, Dict, List
import click
from rich.console import Console
from crewai.cli.authentication.utils import TokenManager
from functools import reduce
from rich.console import Console
from typing import Any, Dict, List
if sys.version_info >= (3, 11):
import tomllib
@@ -55,17 +56,14 @@ def simple_toml_parser(content):
def parse_toml(content):
if sys.version_info >= (3, 11):
return tomllib.loads(content)
else:
return simple_toml_parser(content)
return simple_toml_parser(content)
def get_project_name(
pyproject_path: str = "pyproject.toml", require: bool = False
) -> str | None:
"""Get the project name from the pyproject.toml file."""
return _get_project_attribute(
pyproject_path, ["tool", "poetry", "name"], require=require
)
return _get_project_attribute(pyproject_path, ["project", "name"], require=require)
def get_project_version(
@@ -73,7 +71,7 @@ def get_project_version(
) -> str | None:
"""Get the project version from the pyproject.toml file."""
return _get_project_attribute(
pyproject_path, ["tool", "poetry", "version"], require=require
pyproject_path, ["project", "version"], require=require
)
@@ -82,7 +80,7 @@ def get_project_description(
) -> str | None:
"""Get the project description from the pyproject.toml file."""
return _get_project_attribute(
pyproject_path, ["tool", "poetry", "description"], require=require
pyproject_path, ["project", "description"], require=require
)
@@ -97,10 +95,9 @@ def _get_project_attribute(
pyproject_content = parse_toml(f.read())
dependencies = (
_get_nested_value(pyproject_content, ["tool", "poetry", "dependencies"])
or {}
_get_nested_value(pyproject_content, ["project", "dependencies"]) or []
)
if "crewai" not in dependencies:
if not any(True for dep in dependencies if "crewai" in dep):
raise Exception("crewai is not in the dependencies.")
attribute = _get_nested_value(pyproject_content, keys)

View File

@@ -15,7 +15,10 @@ class EntityMemory(Memory):
storage
if storage
else RAGStorage(
type="entities", allow_reset=False, embedder_config=embedder_config, crew=crew
type="entities",
allow_reset=False,
embedder_config=embedder_config,
crew=crew,
)
)
super().__init__(storage)

View File

@@ -1,21 +1,21 @@
"""Test Agent creation and execution basic functionality."""
import os
from unittest import mock
from unittest.mock import patch
import os
import pytest
from crewai_tools import tool
from crewai import Agent, Crew, Task
from crewai.agents.cache import CacheHandler
from crewai.agents.crew_agent_executor import CrewAgentExecutor
from crewai.agents.parser import AgentAction, CrewAgentParser, OutputParserException
from crewai.llm import LLM
from crewai.agents.parser import CrewAgentParser, OutputParserException
from crewai.tools.tool_calling import InstructorToolCalling
from crewai.tools.tool_usage import ToolUsage
from crewai.tools.tool_usage_events import ToolUsageFinished
from crewai.utilities import RPMController
from crewai_tools import tool
from crewai.agents.parser import AgentAction
from crewai.utilities.events import Emitter
@@ -73,7 +73,7 @@ def test_agent_creation():
def test_agent_default_values():
agent = Agent(role="test role", goal="test goal", backstory="test backstory")
assert agent.llm.model == "gpt-4o"
assert agent.llm.model == "gpt-4o-mini"
assert agent.allow_delegation is False
@@ -116,6 +116,7 @@ def test_custom_llm_temperature_preservation():
@pytest.mark.vcr(filter_headers=["authorization"])
def test_agent_execute_task():
from langchain_openai import ChatOpenAI
from crewai import Task
agent = Agent(
@@ -206,7 +207,7 @@ def test_logging_tool_usage():
verbose=True,
)
assert agent.llm.model == "gpt-4o"
assert agent.llm.model == "gpt-4o-mini"
assert agent.tools_handler.last_used_tool == {}
task = Task(
description="What is 3 times 4?",
@@ -602,6 +603,7 @@ def test_agent_respect_the_max_rpm_set(capsys):
@pytest.mark.vcr(filter_headers=["authorization"])
def test_agent_respect_the_max_rpm_set_over_crew_rpm(capsys):
from unittest.mock import patch
from crewai_tools import tool
@tool
@@ -693,6 +695,7 @@ def test_agent_without_max_rpm_respet_crew_rpm(capsys):
@pytest.mark.vcr(filter_headers=["authorization"])
def test_agent_error_on_parsing_tool(capsys):
from unittest.mock import patch
from crewai_tools import tool
@tool
@@ -855,7 +858,9 @@ def test_agent_function_calling_llm():
tasks = [essay]
crew = Crew(agents=[agent1], tasks=tasks)
from unittest.mock import patch
import instructor
from crewai.tools.tool_usage import ToolUsage
with patch.object(

View File

@@ -1,12 +1,12 @@
import pytest
import requests
import sys
import unittest
from io import StringIO
from requests.exceptions import JSONDecodeError
from unittest.mock import MagicMock, Mock, patch
import pytest
import requests
from requests.exceptions import JSONDecodeError
from crewai.cli.deploy.main import DeployCommand
from crewai.cli.utils import parse_toml
@@ -228,13 +228,11 @@ class TestDeployCommand(unittest.TestCase):
"builtins.open",
new_callable=unittest.mock.mock_open,
read_data="""
[tool.poetry]
[project]
name = "test_project"
version = "0.1.0"
[tool.poetry.dependencies]
python = "^3.10"
crewai = { extras = ["tools"], version = ">=0.51.0,<1.0.0" }
requires-python = ">=3.10,<=3.13"
dependencies = ["crewai"]
""",
)
def test_get_project_name_python_310(self, mock_open):
@@ -248,13 +246,11 @@ class TestDeployCommand(unittest.TestCase):
"builtins.open",
new_callable=unittest.mock.mock_open,
read_data="""
[tool.poetry]
[project]
name = "test_project"
version = "0.1.0"
[tool.poetry.dependencies]
python = "^3.11"
crewai = { extras = ["tools"], version = ">=0.51.0,<1.0.0" }
requires-python = ">=3.10,<=3.13"
dependencies = ["crewai"]
""",
)
def test_get_project_name_python_311_plus(self, mock_open):

View File

@@ -18,12 +18,12 @@ from crewai.cli import evaluate_crew
def test_crew_success(mock_subprocess_run, n_iterations, model):
"""Test the crew function for successful execution."""
mock_subprocess_run.return_value = subprocess.CompletedProcess(
args=f"poetry run test {n_iterations} {model}", returncode=0
args=f"uv run test {n_iterations} {model}", returncode=0
)
result = evaluate_crew.evaluate_crew(n_iterations, model)
mock_subprocess_run.assert_called_once_with(
["poetry", "run", "test", str(n_iterations), model],
["uv", "run", "test", str(n_iterations), model],
capture_output=False,
text=True,
check=True,
@@ -55,14 +55,14 @@ def test_test_crew_called_process_error(mock_subprocess_run, click):
n_iterations = 5
mock_subprocess_run.side_effect = subprocess.CalledProcessError(
returncode=1,
cmd=["poetry", "run", "test", str(n_iterations), "gpt-4o"],
cmd=["uv", "run", "test", str(n_iterations), "gpt-4o"],
output="Error",
stderr="Some error occurred",
)
evaluate_crew.evaluate_crew(n_iterations, "gpt-4o")
mock_subprocess_run.assert_called_once_with(
["poetry", "run", "test", "5", "gpt-4o"],
["uv", "run", "test", "5", "gpt-4o"],
capture_output=False,
text=True,
check=True,
@@ -70,7 +70,7 @@ def test_test_crew_called_process_error(mock_subprocess_run, click):
click.echo.assert_has_calls(
[
mock.call.echo(
"An error occurred while testing the crew: Command '['poetry', 'run', 'test', '5', 'gpt-4o']' returned non-zero exit status 1.",
"An error occurred while testing the crew: Command '['uv', 'run', 'test', '5', 'gpt-4o']' returned non-zero exit status 1.",
err=True,
),
mock.call.echo("Error", err=True),
@@ -87,7 +87,7 @@ def test_test_crew_unexpected_exception(mock_subprocess_run, click):
evaluate_crew.evaluate_crew(n_iterations, "gpt-4o")
mock_subprocess_run.assert_called_once_with(
["poetry", "run", "test", "5", "gpt-4o"],
["uv", "run", "test", "5", "gpt-4o"],
capture_output=False,
text=True,
check=True,

View File

@@ -1,13 +1,16 @@
import os
import tempfile
import unittest
import unittest.mock
import os
from contextlib import contextmanager
from io import StringIO
from unittest import mock
from unittest.mock import MagicMock, patch
from pytest import raises
from crewai.cli.tools.main import ToolCommand
from io import StringIO
from unittest.mock import patch, MagicMock
@contextmanager
def in_temp_dir():
@@ -19,6 +22,7 @@ def in_temp_dir():
finally:
os.chdir(original_dir)
@patch("crewai.cli.tools.main.subprocess.run")
def test_create_success(mock_subprocess):
with in_temp_dir():
@@ -38,9 +42,7 @@ def test_create_success(mock_subprocess):
)
assert os.path.isfile(os.path.join("test_tool", "src", "test_tool", "tool.py"))
with open(
os.path.join("test_tool", "src", "test_tool", "tool.py"), "r"
) as f:
with open(os.path.join("test_tool", "src", "test_tool", "tool.py"), "r") as f:
content = f.read()
assert "class TestTool" in content
@@ -49,6 +51,7 @@ def test_create_success(mock_subprocess):
assert "Creating custom tool test_tool..." in output
@patch("crewai.cli.tools.main.subprocess.run")
@patch("crewai.cli.plus_api.PlusAPI.get_tool")
def test_install_success(mock_get, mock_subprocess_run):
@@ -67,9 +70,15 @@ def test_install_success(mock_get, mock_subprocess_run):
tool_command.install("sample-tool")
output = fake_out.getvalue()
mock_get.assert_called_once_with("sample-tool")
mock_get.assert_has_calls([mock.call("sample-tool"), mock.call().json()])
mock_subprocess_run.assert_any_call(
["poetry", "add", "--source", "crewai-sample-repo", "sample-tool"],
[
"uv",
"add",
"--extra-index-url",
"https://app.crewai.com/pypi/sample-repo",
"sample-tool",
],
capture_output=False,
text=True,
check=True,
@@ -77,6 +86,7 @@ def test_install_success(mock_get, mock_subprocess_run):
assert "Succesfully installed sample-tool" in output
@patch("crewai.cli.plus_api.PlusAPI.get_tool")
def test_install_tool_not_found(mock_get):
mock_get_response = MagicMock()
@@ -95,6 +105,7 @@ def test_install_tool_not_found(mock_get):
mock_get.assert_called_once_with("non-existent-tool")
assert "No tool found with this name" in output
@patch("crewai.cli.plus_api.PlusAPI.get_tool")
def test_install_api_error(mock_get):
mock_get_response = MagicMock()
@@ -113,15 +124,16 @@ def test_install_api_error(mock_get):
mock_get.assert_called_once_with("error-tool")
assert "Failed to get tool details" in output
@patch("crewai.cli.tools.main.git.Repository.is_synced", return_value=False)
def test_publish_when_not_in_sync(mock_is_synced):
with patch("sys.stdout", new=StringIO()) as fake_out, \
raises(SystemExit):
with patch("sys.stdout", new=StringIO()) as fake_out, raises(SystemExit):
tool_command = ToolCommand()
tool_command.publish(is_public=True)
assert "Local changes need to be resolved before publishing" in fake_out.getvalue()
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
@patch("crewai.cli.tools.main.get_project_version", return_value="1.0.0")
@patch("crewai.cli.tools.main.get_project_description", return_value="A sample tool")
@@ -156,7 +168,7 @@ def test_publish_when_not_in_sync_and_force(
mock_get_project_version.assert_called_with(require=True)
mock_get_project_description.assert_called_with(require=False)
mock_subprocess_run.assert_called_with(
["poetry", "build", "-f", "sdist", "--output", unittest.mock.ANY],
["uv", "build", "--sdist", "--out-dir", unittest.mock.ANY],
check=True,
capture_output=False,
)
@@ -169,6 +181,7 @@ def test_publish_when_not_in_sync_and_force(
encoded_file=unittest.mock.ANY,
)
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
@patch("crewai.cli.tools.main.get_project_version", return_value="1.0.0")
@patch("crewai.cli.tools.main.get_project_description", return_value="A sample tool")
@@ -203,7 +216,7 @@ def test_publish_success(
mock_get_project_version.assert_called_with(require=True)
mock_get_project_description.assert_called_with(require=False)
mock_subprocess_run.assert_called_with(
["poetry", "build", "-f", "sdist", "--output", unittest.mock.ANY],
["uv", "build", "--sdist", "--out-dir", unittest.mock.ANY],
check=True,
capture_output=False,
)
@@ -216,6 +229,7 @@ def test_publish_success(
encoded_file=unittest.mock.ANY,
)
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
@patch("crewai.cli.tools.main.get_project_version", return_value="1.0.0")
@patch("crewai.cli.tools.main.get_project_description", return_value="A sample tool")
@@ -254,6 +268,7 @@ def test_publish_failure(
assert "Failed to complete operation" in output
assert "Name is already taken" in output
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
@patch("crewai.cli.tools.main.get_project_version", return_value="1.0.0")
@patch("crewai.cli.tools.main.get_project_description", return_value="A sample tool")
@@ -291,54 +306,3 @@ def test_publish_api_error(
mock_publish.assert_called_once()
assert "Request to Enterprise API failed" in output
@patch("crewai.cli.plus_api.PlusAPI.login_to_tool_repository")
@patch("crewai.cli.tools.main.subprocess.run")
def test_login_success(mock_subprocess_run, mock_login):
mock_login_response = MagicMock()
mock_login_response.status_code = 200
mock_login_response.json.return_value = {
"repositories": [
{
"handle": "tools",
"url": "https://example.com/repo",
}
],
"credential": {"username": "user", "password": "pass"},
}
mock_login.return_value = mock_login_response
mock_subprocess_run.return_value = MagicMock(stderr=None)
tool_command = ToolCommand()
with patch("sys.stdout", new=StringIO()) as fake_out:
tool_command.login()
output = fake_out.getvalue()
mock_login.assert_called_once()
mock_subprocess_run.assert_any_call(
[
"poetry",
"source",
"add",
"--priority=explicit",
"crewai-tools",
"https://example.com/repo",
],
text=True,
check=True,
)
mock_subprocess_run.assert_any_call(
[
"poetry",
"config",
"http-basic.crewai-tools",
"user",
"pass",
],
capture_output=False,
text=True,
check=True,
)
assert "Succesfully authenticated to the tool repository" in output

View File

@@ -8,7 +8,7 @@ from crewai.cli.train_crew import train_crew
def test_train_crew_positive_iterations(mock_subprocess_run):
n_iterations = 5
mock_subprocess_run.return_value = subprocess.CompletedProcess(
args=["poetry", "run", "train", str(n_iterations)],
args=["uv", "run", "train", str(n_iterations)],
returncode=0,
stdout="Success",
stderr="",
@@ -17,7 +17,7 @@ def test_train_crew_positive_iterations(mock_subprocess_run):
train_crew(n_iterations, "trained_agents_data.pkl")
mock_subprocess_run.assert_called_once_with(
["poetry", "run", "train", str(n_iterations), "trained_agents_data.pkl"],
["uv", "run", "train", str(n_iterations), "trained_agents_data.pkl"],
capture_output=False,
text=True,
check=True,
@@ -48,14 +48,14 @@ def test_train_crew_called_process_error(mock_subprocess_run, click):
n_iterations = 5
mock_subprocess_run.side_effect = subprocess.CalledProcessError(
returncode=1,
cmd=["poetry", "run", "train", str(n_iterations)],
cmd=["uv", "run", "train", str(n_iterations)],
output="Error",
stderr="Some error occurred",
)
train_crew(n_iterations, "trained_agents_data.pkl")
mock_subprocess_run.assert_called_once_with(
["poetry", "run", "train", str(n_iterations), "trained_agents_data.pkl"],
["uv", "run", "train", str(n_iterations), "trained_agents_data.pkl"],
capture_output=False,
text=True,
check=True,
@@ -63,7 +63,7 @@ def test_train_crew_called_process_error(mock_subprocess_run, click):
click.echo.assert_has_calls(
[
mock.call.echo(
"An error occurred while training the crew: Command '['poetry', 'run', 'train', '5']' returned non-zero exit status 1.",
"An error occurred while training the crew: Command '['uv', 'run', 'train', '5']' returned non-zero exit status 1.",
err=True,
),
mock.call.echo("Error", err=True),
@@ -79,7 +79,7 @@ def test_train_crew_unexpected_exception(mock_subprocess_run, click):
train_crew(n_iterations, "trained_agents_data.pkl")
mock_subprocess_run.assert_called_once_with(
["poetry", "run", "train", str(n_iterations), "trained_agents_data.pkl"],
["uv", "run", "train", str(n_iterations), "trained_agents_data.pkl"],
capture_output=False,
text=True,
check=True,

4972
uv.lock generated Normal file

File diff suppressed because it is too large Load Diff