mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-02 21:58:11 +00:00
Consolidate crewai run and crewai flow kickoff (#6296)
Make `crewai run` the single execution path for crews and flows, with `crewai flow kickoff` kept as a deprecated compatibility alias.
This commit is contained in:
@@ -12,6 +12,7 @@ from crewai_cli.cli import (
|
||||
deploy_remove,
|
||||
deply_status,
|
||||
flow_add_crew,
|
||||
flow_run,
|
||||
login,
|
||||
reset_memories,
|
||||
run,
|
||||
@@ -126,40 +127,72 @@ def test_run_uses_project_runner_by_default(run_crew, runner):
|
||||
result = runner.invoke(run)
|
||||
|
||||
assert result.exit_code == 0
|
||||
run_crew.assert_called_once_with(trained_agents_file=None)
|
||||
run_crew.assert_called_once_with(
|
||||
trained_agents_file=None,
|
||||
definition=None,
|
||||
inputs=None,
|
||||
)
|
||||
assert "experimental" not in result.output.lower()
|
||||
|
||||
|
||||
@mock.patch("crewai_cli.cli.run_declarative_flow")
|
||||
def test_run_with_definition_uses_definition_runner(run_declarative_flow, runner):
|
||||
@mock.patch("crewai_cli.cli.run_crew")
|
||||
def test_run_with_definition_uses_project_runner(run_crew, runner):
|
||||
result = runner.invoke(
|
||||
run,
|
||||
["--definition", "flow.yaml", "--inputs", '{"topic":"AI"}'],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert (
|
||||
"Warning: `crewai run --definition` is experimental and may change without notice."
|
||||
in result.output
|
||||
)
|
||||
run_declarative_flow.assert_called_once_with(
|
||||
definition="flow.yaml", inputs='{"topic":"AI"}'
|
||||
run_crew.assert_called_once_with(
|
||||
trained_agents_file=None,
|
||||
definition="flow.yaml",
|
||||
inputs='{"topic":"AI"}',
|
||||
)
|
||||
|
||||
|
||||
@mock.patch("crewai_cli.cli.run_crew")
|
||||
@mock.patch("crewai_cli.cli.run_declarative_flow")
|
||||
def test_run_rejects_inputs_without_definition(
|
||||
run_declarative_flow, run_crew, runner
|
||||
):
|
||||
def test_run_rejects_inputs_without_definition(run_crew, runner):
|
||||
result = runner.invoke(run, ["--inputs", '{"topic":"AI"}'])
|
||||
|
||||
assert result.exit_code == 2
|
||||
assert "Error: --inputs requires --definition" in result.output
|
||||
run_declarative_flow.assert_not_called()
|
||||
run_crew.assert_not_called()
|
||||
|
||||
|
||||
@mock.patch("crewai_cli.cli.run_crew")
|
||||
def test_run_rejects_filename_with_definition(run_crew, runner):
|
||||
result = runner.invoke(run, ["--definition", "flow.yaml", "--filename", "x.pkl"])
|
||||
|
||||
assert result.exit_code == 2
|
||||
assert "Error: --filename can only be used when running crews" in result.output
|
||||
run_crew.assert_not_called()
|
||||
|
||||
|
||||
@mock.patch("crewai_cli.cli.run_crew")
|
||||
def test_run_passes_filename_to_project_runner(run_crew, runner):
|
||||
result = runner.invoke(run, ["--filename", "trained.pkl"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
run_crew.assert_called_once_with(
|
||||
trained_agents_file="trained.pkl",
|
||||
definition=None,
|
||||
inputs=None,
|
||||
)
|
||||
|
||||
|
||||
@mock.patch("crewai_cli.cli.run_crew")
|
||||
def test_flow_kickoff_is_deprecated_and_uses_run_path(run_crew, runner):
|
||||
result = runner.invoke(flow_run)
|
||||
|
||||
assert result.exit_code == 0
|
||||
run_crew.assert_called_once_with(
|
||||
trained_agents_file=None,
|
||||
definition=None,
|
||||
inputs=None,
|
||||
)
|
||||
assert "DeprecationWarning" in result.output
|
||||
|
||||
|
||||
@mock.patch("crewai_cli.create_json_crew.create_json_crew")
|
||||
def test_create_crew_in_dmn_mode_skips_provider_prompts(create_json_crew, runner):
|
||||
result = runner.invoke(create, ["crew", "DMN Crew"], env={"CREWAI_DMN": "True"})
|
||||
|
||||
@@ -28,9 +28,7 @@ def test_create_flow_declarative_project_can_run(
|
||||
assert (project_root / pyproject["tool"]["crewai"]["definition"]).is_file()
|
||||
|
||||
monkeypatch.chdir(project_root)
|
||||
result = CliRunner().invoke(
|
||||
crewai, ["flow", "kickoff"], env={"UV_RUN_RECURSION_DEPTH": "1"}
|
||||
)
|
||||
result = CliRunner().invoke(crewai, ["run"], env={"UV_RUN_RECURSION_DEPTH": "1"})
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "Running the Flow" in result.output
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
import crewai_cli.kickoff_flow as kickoff_flow_module
|
||||
from crewai_cli.cli import flow_run
|
||||
import crewai_cli.plot_flow as plot_flow_module
|
||||
|
||||
|
||||
@@ -33,18 +33,19 @@ def _write_flow_project(project_root: Path) -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_kickoff_flow_runs_configured_declarative_definition(
|
||||
def test_flow_kickoff_runs_configured_declarative_definition(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
_write_flow_project(tmp_path)
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setenv("UV_RUN_RECURSION_DEPTH", "1")
|
||||
|
||||
kickoff_flow_module.kickoff_flow()
|
||||
result = CliRunner().invoke(flow_run)
|
||||
|
||||
assert capsys.readouterr().out == "AI\n"
|
||||
assert result.exit_code == 0
|
||||
assert "DeprecationWarning" in result.output
|
||||
assert "Running the Flow\nAI\n" in result.output
|
||||
|
||||
|
||||
def test_plot_flow_runs_configured_declarative_definition(
|
||||
@@ -57,18 +58,27 @@ def test_plot_flow_runs_configured_declarative_definition(
|
||||
plot_flow_module.plot_flow()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("command", "expected"),
|
||||
[
|
||||
pytest.param(kickoff_flow_module.kickoff_flow, ["uv", "run", "kickoff"]),
|
||||
pytest.param(plot_flow_module.plot_flow, ["uv", "run", "plot"]),
|
||||
],
|
||||
)
|
||||
def test_flow_commands_keep_python_entrypoint_without_definition(
|
||||
def test_flow_kickoff_delegates_to_run_crew(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
calls = []
|
||||
|
||||
monkeypatch.setattr(
|
||||
"crewai_cli.cli.run_crew",
|
||||
lambda **kwargs: calls.append(kwargs),
|
||||
)
|
||||
|
||||
result = CliRunner().invoke(flow_run)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert calls == [
|
||||
{"trained_agents_file": None, "definition": None, "inputs": None},
|
||||
]
|
||||
|
||||
|
||||
def test_plot_flow_keeps_python_entrypoint_without_definition(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
command: Callable[[], None],
|
||||
expected: list[str],
|
||||
) -> None:
|
||||
subprocess_calls = []
|
||||
|
||||
@@ -79,11 +89,11 @@ def test_flow_commands_keep_python_entrypoint_without_definition(
|
||||
lambda command, **kwargs: subprocess_calls.append((command, kwargs)),
|
||||
)
|
||||
|
||||
command()
|
||||
plot_flow_module.plot_flow()
|
||||
|
||||
assert subprocess_calls == [
|
||||
(
|
||||
expected,
|
||||
["uv", "run", "plot"],
|
||||
{"capture_output": False, "text": True, "check": True},
|
||||
)
|
||||
]
|
||||
|
||||
@@ -568,3 +568,131 @@ def test_has_json_crew_true_without_pyproject(monkeypatch, tmp_path: Path):
|
||||
(tmp_path / "crew.jsonc").write_text("{}")
|
||||
|
||||
assert run_crew_module._has_json_crew() is True
|
||||
|
||||
|
||||
def test_run_crew_rejects_inputs_without_definition():
|
||||
with pytest.raises(click.UsageError) as exc_info:
|
||||
run_crew_module.run_crew(inputs='{"topic":"AI"}')
|
||||
|
||||
assert "--inputs requires --definition" in exc_info.value.message
|
||||
|
||||
|
||||
def test_run_crew_rejects_filename_with_explicit_definition():
|
||||
with pytest.raises(click.UsageError) as exc_info:
|
||||
run_crew_module.run_crew(
|
||||
trained_agents_file="trained.pkl",
|
||||
definition="flow.yaml",
|
||||
)
|
||||
|
||||
assert "--filename can only be used when running crews" in exc_info.value.message
|
||||
|
||||
|
||||
def test_run_crew_runs_explicit_declarative_definition(monkeypatch, capsys):
|
||||
calls = []
|
||||
|
||||
def fake_run_declarative_flow(definition: str, inputs: str | None = None):
|
||||
calls.append((definition, inputs))
|
||||
|
||||
monkeypatch.setattr(
|
||||
"crewai_cli.run_declarative_flow.run_declarative_flow",
|
||||
fake_run_declarative_flow,
|
||||
)
|
||||
|
||||
run_crew_module.run_crew(definition="flow.yaml", inputs='{"topic":"AI"}')
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "experimental" not in captured.out.lower()
|
||||
assert calls == [("flow.yaml", '{"topic":"AI"}')]
|
||||
|
||||
|
||||
def test_run_crew_runs_classic_crew_project(monkeypatch, capsys):
|
||||
calls = []
|
||||
|
||||
monkeypatch.setattr(run_crew_module, "_has_json_crew", lambda: False)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"read_toml",
|
||||
lambda: {"tool": {"crewai": {"type": "crew"}}},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"_execute_uv_script",
|
||||
lambda script_name, **kwargs: calls.append((script_name, kwargs)),
|
||||
)
|
||||
|
||||
run_crew_module.run_crew(trained_agents_file="trained.pkl")
|
||||
|
||||
assert capsys.readouterr().out == "Running the Crew\n"
|
||||
assert calls == [
|
||||
(
|
||||
"run_crew",
|
||||
{"entity_type": "crew", "trained_agents_file": "trained.pkl"},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def test_run_crew_runs_python_flow_project(monkeypatch, capsys):
|
||||
calls = []
|
||||
|
||||
monkeypatch.setattr(run_crew_module, "_has_json_crew", lambda: False)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"read_toml",
|
||||
lambda: {"tool": {"crewai": {"type": "flow"}}},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"_execute_uv_script",
|
||||
lambda script_name, **kwargs: calls.append((script_name, kwargs)),
|
||||
)
|
||||
|
||||
run_crew_module.run_crew()
|
||||
|
||||
assert capsys.readouterr().out == "Running the Flow\n"
|
||||
assert calls == [("kickoff", {"entity_type": "flow"})]
|
||||
|
||||
|
||||
def test_run_crew_rejects_filename_for_flow_project(monkeypatch):
|
||||
monkeypatch.setattr(run_crew_module, "_has_json_crew", lambda: False)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"read_toml",
|
||||
lambda: {"tool": {"crewai": {"type": "flow"}}},
|
||||
)
|
||||
|
||||
with pytest.raises(click.UsageError) as exc_info:
|
||||
run_crew_module.run_crew(trained_agents_file="trained.pkl")
|
||||
|
||||
assert "--filename can only be used when running crews" in exc_info.value.message
|
||||
|
||||
|
||||
def test_run_crew_runs_configured_declarative_flow_project(monkeypatch, capsys):
|
||||
calls = []
|
||||
|
||||
monkeypatch.setattr(run_crew_module, "_has_json_crew", lambda: False)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"read_toml",
|
||||
lambda: {
|
||||
"tool": {
|
||||
"crewai": {
|
||||
"type": "flow",
|
||||
"definition": "flow.yaml",
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"crewai_cli.run_declarative_flow.run_declarative_flow_in_project_env",
|
||||
lambda definition, inputs=None: calls.append((definition, inputs)),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"_execute_uv_script",
|
||||
lambda *_args, **_kwargs: pytest.fail("declarative flows must not run kickoff"),
|
||||
)
|
||||
|
||||
run_crew_module.run_crew()
|
||||
|
||||
assert capsys.readouterr().out == "Running the Flow\n"
|
||||
assert calls == [("flow.yaml", None)]
|
||||
|
||||
@@ -83,7 +83,7 @@ def test_run_declarative_flow_in_project_env_uses_uv(
|
||||
|
||||
assert subprocess_calls == [
|
||||
(
|
||||
["uv", "run", "crewai", "flow", "kickoff"],
|
||||
["uv", "run", "crewai", "run"],
|
||||
{
|
||||
"capture_output": False,
|
||||
"text": True,
|
||||
|
||||
Reference in New Issue
Block a user