mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-04-30 14:52:36 +00:00
Compare commits
1 Commits
devin/1751
...
lg-llm-doc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3338951763 |
@@ -684,28 +684,6 @@ In this section, you'll find detailed examples that help you select, configure,
|
||||
- openrouter/deepseek/deepseek-chat
|
||||
</Info>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Nebius AI Studio">
|
||||
Set the following environment variables in your `.env` file:
|
||||
```toml Code
|
||||
NEBIUS_API_KEY=<your-api-key>
|
||||
```
|
||||
|
||||
Example usage in your CrewAI project:
|
||||
```python Code
|
||||
llm = LLM(
|
||||
model="nebius/Qwen/Qwen3-30B-A3B"
|
||||
)
|
||||
```
|
||||
|
||||
<Info>
|
||||
Nebius AI Studio features:
|
||||
- Large collection of open source models
|
||||
- Higher rate limits
|
||||
- Competitive pricing
|
||||
- Good balance of speed and quality
|
||||
</Info>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Streaming Responses
|
||||
|
||||
@@ -34,7 +34,6 @@ LiteLLM supports a wide range of providers, including but not limited to:
|
||||
- DeepInfra
|
||||
- Groq
|
||||
- SambaNova
|
||||
- Nebius AI Studio
|
||||
- [NVIDIA NIMs](https://docs.api.nvidia.com/nim/reference/models-1)
|
||||
- And many more!
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "crewai"
|
||||
dynamic = ["version"]
|
||||
version = "0.134.0"
|
||||
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."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10,<3.14"
|
||||
@@ -117,9 +117,6 @@ torchvision = [
|
||||
{ index = "pytorch", marker = "python_version < '3.13'" },
|
||||
]
|
||||
|
||||
[tool.hatch.version]
|
||||
path = "src/crewai/__init__.py"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
@@ -31,5 +31,4 @@ __all__ = [
|
||||
"Knowledge",
|
||||
"TaskOutput",
|
||||
"LLMGuardrail",
|
||||
"__version__",
|
||||
]
|
||||
|
||||
@@ -71,9 +71,6 @@ class Agent(BaseAgent):
|
||||
"""
|
||||
|
||||
_times_executed: int = PrivateAttr(default=0)
|
||||
_knowledge_loaded: bool = PrivateAttr(default=False)
|
||||
_last_embedder: Optional[Dict[str, Any]] = PrivateAttr(default=None)
|
||||
_last_knowledge_sources: Optional[List[Any]] = PrivateAttr(default=None)
|
||||
max_execution_time: Optional[int] = Field(
|
||||
default=None,
|
||||
description="Maximum execution time for an agent to execute a task",
|
||||
@@ -197,13 +194,6 @@ class Agent(BaseAgent):
|
||||
|
||||
def set_knowledge(self, crew_embedder: Optional[Dict[str, Any]] = None):
|
||||
try:
|
||||
current_embedder = crew_embedder or self.embedder
|
||||
if (self._knowledge_loaded and
|
||||
self.knowledge is not None and
|
||||
self._last_embedder == current_embedder and
|
||||
self._last_knowledge_sources == self.knowledge_sources):
|
||||
return
|
||||
|
||||
if self.embedder is None and crew_embedder:
|
||||
self.embedder = crew_embedder
|
||||
|
||||
@@ -218,10 +208,6 @@ class Agent(BaseAgent):
|
||||
storage=self.knowledge_storage or None,
|
||||
)
|
||||
self.knowledge.add_sources()
|
||||
|
||||
self._knowledge_loaded = True
|
||||
self._last_embedder = current_embedder
|
||||
self._last_knowledge_sources = self.knowledge_sources.copy() if self.knowledge_sources else None
|
||||
except (TypeError, ValueError) as e:
|
||||
raise ValueError(f"Invalid Knowledge Configuration: {str(e)}")
|
||||
|
||||
@@ -242,13 +228,6 @@ class Agent(BaseAgent):
|
||||
|
||||
return any(getattr(self.crew, attr) for attr in memory_attributes)
|
||||
|
||||
def reset_knowledge_cache(self) -> None:
|
||||
"""Reset the knowledge cache to force reloading on next set_knowledge call."""
|
||||
self._knowledge_loaded = False
|
||||
self._last_embedder = None
|
||||
self._last_knowledge_sources = None
|
||||
self.knowledge = None
|
||||
|
||||
def execute_task(
|
||||
self,
|
||||
task: Task,
|
||||
|
||||
@@ -5,6 +5,8 @@ from typing import Any, Dict
|
||||
import requests
|
||||
from rich.console import Console
|
||||
|
||||
from crewai.cli.tools.main import ToolCommand
|
||||
|
||||
from .constants import AUTH0_AUDIENCE, AUTH0_CLIENT_ID, AUTH0_DOMAIN
|
||||
from .utils import TokenManager, validate_token
|
||||
|
||||
@@ -65,7 +67,6 @@ class AuthenticationCommand:
|
||||
self.token_manager.save_tokens(token_data["access_token"], expires_in)
|
||||
|
||||
try:
|
||||
from crewai.cli.tools.main import ToolCommand
|
||||
ToolCommand().login()
|
||||
except Exception:
|
||||
console.print(
|
||||
|
||||
@@ -14,50 +14,8 @@ from crewai.cli.utils import copy_template, load_env_vars, write_env_file
|
||||
|
||||
|
||||
def create_folder_structure(name, parent_folder=None):
|
||||
import keyword
|
||||
import re
|
||||
|
||||
name = name.rstrip('/')
|
||||
|
||||
if not name.strip():
|
||||
raise ValueError("Project name cannot be empty or contain only whitespace")
|
||||
|
||||
folder_name = name.replace(" ", "_").replace("-", "_").lower()
|
||||
folder_name = re.sub(r'[^a-zA-Z0-9_]', '', folder_name)
|
||||
|
||||
# Check if the name starts with invalid characters or is primarily invalid
|
||||
if re.match(r'^[^a-zA-Z0-9_-]+', name):
|
||||
raise ValueError(f"Project name '{name}' contains no valid characters for a Python module name")
|
||||
|
||||
if not folder_name:
|
||||
raise ValueError(f"Project name '{name}' contains no valid characters for a Python module name")
|
||||
|
||||
if folder_name[0].isdigit():
|
||||
raise ValueError(f"Project name '{name}' would generate folder name '{folder_name}' which cannot start with a digit (invalid Python module name)")
|
||||
|
||||
if keyword.iskeyword(folder_name):
|
||||
raise ValueError(f"Project name '{name}' would generate folder name '{folder_name}' which is a reserved Python keyword")
|
||||
|
||||
if not folder_name.isidentifier():
|
||||
raise ValueError(f"Project name '{name}' would generate invalid Python module name '{folder_name}'")
|
||||
|
||||
class_name = name.replace("_", " ").replace("-", " ").title().replace(" ", "")
|
||||
|
||||
class_name = re.sub(r'[^a-zA-Z0-9_]', '', class_name)
|
||||
|
||||
if not class_name:
|
||||
raise ValueError(f"Project name '{name}' contains no valid characters for a Python class name")
|
||||
|
||||
if class_name[0].isdigit():
|
||||
raise ValueError(f"Project name '{name}' would generate class name '{class_name}' which cannot start with a digit")
|
||||
|
||||
# Check if the original name (before title casing) is a keyword
|
||||
original_name_clean = re.sub(r'[^a-zA-Z0-9_]', '', name.replace("_", "").replace("-", "").lower())
|
||||
if keyword.iskeyword(original_name_clean) or keyword.iskeyword(class_name) or class_name in ('True', 'False', 'None'):
|
||||
raise ValueError(f"Project name '{name}' would generate class name '{class_name}' which is a reserved Python keyword")
|
||||
|
||||
if not class_name.isidentifier():
|
||||
raise ValueError(f"Project name '{name}' would generate invalid Python class name '{class_name}'")
|
||||
|
||||
if parent_folder:
|
||||
folder_path = Path(parent_folder) / folder_name
|
||||
|
||||
@@ -252,7 +252,7 @@ def write_env_file(folder_path, env_vars):
|
||||
env_file_path = folder_path / ".env"
|
||||
with open(env_file_path, "w") as file:
|
||||
for key, value in env_vars.items():
|
||||
file.write(f"{key.upper()}={value}\n")
|
||||
file.write(f"{key}={value}\n")
|
||||
|
||||
|
||||
def get_crews(crew_path: str = "crew.py", require: bool = False) -> list[Crew]:
|
||||
|
||||
@@ -2276,112 +2276,3 @@ def test_agent_from_repository_without_org_set(
|
||||
"No organization currently set. We recommend setting one before using: `crewai org switch <org_id>` command.",
|
||||
style="yellow",
|
||||
)
|
||||
|
||||
|
||||
def test_agent_knowledge_caching_on_multiple_set_knowledge_calls():
|
||||
"""Test that agent knowledge is only loaded once when set_knowledge is called multiple times."""
|
||||
content = "Brandon's favorite color is blue and he likes Mexican food."
|
||||
string_source = StringKnowledgeSource(content=content)
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Research about Brandon",
|
||||
backstory="You are a researcher.",
|
||||
knowledge_sources=[string_source],
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
with patch('crewai.agent.Knowledge') as mock_knowledge_class:
|
||||
mock_knowledge_instance = MagicMock()
|
||||
mock_knowledge_class.return_value = mock_knowledge_instance
|
||||
|
||||
agent.set_knowledge()
|
||||
assert mock_knowledge_class.call_count == 1, "Knowledge should be created once on first call"
|
||||
assert mock_knowledge_instance.add_sources.call_count == 1, "add_sources should be called once"
|
||||
|
||||
agent.set_knowledge()
|
||||
assert mock_knowledge_class.call_count == 1, "Knowledge should not be recreated on second call"
|
||||
assert mock_knowledge_instance.add_sources.call_count == 1, "add_sources should not be called again"
|
||||
|
||||
agent.set_knowledge()
|
||||
assert mock_knowledge_class.call_count == 1, "Knowledge should not be recreated on third call"
|
||||
assert mock_knowledge_instance.add_sources.call_count == 1, "add_sources should not be called again"
|
||||
|
||||
|
||||
def test_agent_knowledge_reloads_when_sources_change():
|
||||
"""Test that agent knowledge is reloaded when knowledge sources change."""
|
||||
content1 = "Brandon's favorite color is blue."
|
||||
content2 = "Brandon's favorite food is tacos."
|
||||
string_source1 = StringKnowledgeSource(content=content1)
|
||||
string_source2 = StringKnowledgeSource(content=content2)
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Research about Brandon",
|
||||
backstory="You are a researcher.",
|
||||
knowledge_sources=[string_source1],
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
with patch('crewai.agent.Knowledge') as mock_knowledge_class:
|
||||
mock_knowledge_instance = MagicMock()
|
||||
mock_knowledge_class.return_value = mock_knowledge_instance
|
||||
|
||||
agent.set_knowledge()
|
||||
assert mock_knowledge_class.call_count == 1, "Knowledge should be created once on first call"
|
||||
|
||||
agent.knowledge_sources = [string_source2]
|
||||
|
||||
agent.set_knowledge()
|
||||
assert mock_knowledge_class.call_count == 2, "Knowledge should be recreated when sources change"
|
||||
|
||||
|
||||
def test_agent_knowledge_reloads_when_embedder_changes():
|
||||
"""Test that agent knowledge is reloaded when embedder changes."""
|
||||
content = "Brandon's favorite color is blue."
|
||||
string_source = StringKnowledgeSource(content=content)
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Research about Brandon",
|
||||
backstory="You are a researcher.",
|
||||
knowledge_sources=[string_source],
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
embedder1 = {"provider": "openai", "model": "text-embedding-ada-002"}
|
||||
embedder2 = {"provider": "openai", "model": "text-embedding-3-small"}
|
||||
|
||||
with patch('crewai.agent.Knowledge') as mock_knowledge_class:
|
||||
mock_knowledge_instance = MagicMock()
|
||||
mock_knowledge_class.return_value = mock_knowledge_instance
|
||||
|
||||
agent.set_knowledge(crew_embedder=embedder1)
|
||||
assert mock_knowledge_class.call_count == 1, "Knowledge should be created once on first call"
|
||||
|
||||
agent.set_knowledge(crew_embedder=embedder2)
|
||||
assert mock_knowledge_class.call_count == 2, "Knowledge should be recreated when embedder changes"
|
||||
|
||||
|
||||
def test_agent_reset_knowledge_cache():
|
||||
"""Test that reset_knowledge_cache forces knowledge reloading."""
|
||||
content = "Brandon's favorite color is blue."
|
||||
string_source = StringKnowledgeSource(content=content)
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Research about Brandon",
|
||||
backstory="You are a researcher.",
|
||||
knowledge_sources=[string_source]
|
||||
)
|
||||
|
||||
agent._knowledge_loaded = True
|
||||
agent._last_embedder = {"model": "test"}
|
||||
agent._last_knowledge_sources = [string_source]
|
||||
|
||||
agent.reset_knowledge_cache()
|
||||
|
||||
assert not getattr(agent, '_knowledge_loaded', True)
|
||||
assert agent._last_embedder is None
|
||||
assert agent._last_knowledge_sources is None
|
||||
assert agent.knowledge is None
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -44,7 +44,7 @@ class TestAuthenticationCommand(unittest.TestCase):
|
||||
mock_print.assert_any_call("2. Enter the following code: ", "ABCDEF")
|
||||
mock_open.assert_called_once_with("https://example.com")
|
||||
|
||||
@patch("crewai.cli.tools.main.ToolCommand")
|
||||
@patch("crewai.cli.authentication.main.ToolCommand")
|
||||
@patch("crewai.cli.authentication.main.requests.post")
|
||||
@patch("crewai.cli.authentication.main.validate_token")
|
||||
@patch("crewai.cli.authentication.main.console.print")
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
import keyword
|
||||
import shutil
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from crewai.cli.create_crew import create_crew, create_folder_structure
|
||||
|
||||
@pytest.fixture
|
||||
def runner():
|
||||
return CliRunner()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_dir():
|
||||
temp_path = tempfile.mkdtemp()
|
||||
yield temp_path
|
||||
shutil.rmtree(temp_path)
|
||||
|
||||
|
||||
def test_create_folder_structure_strips_single_trailing_slash():
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
folder_path, folder_name, class_name = create_folder_structure("hello/", parent_folder=temp_dir)
|
||||
|
||||
assert folder_name == "hello"
|
||||
assert class_name == "Hello"
|
||||
assert folder_path.name == "hello"
|
||||
assert folder_path.exists()
|
||||
assert folder_path.parent == Path(temp_dir)
|
||||
|
||||
|
||||
def test_create_folder_structure_strips_multiple_trailing_slashes():
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
folder_path, folder_name, class_name = create_folder_structure("hello///", parent_folder=temp_dir)
|
||||
|
||||
assert folder_name == "hello"
|
||||
assert class_name == "Hello"
|
||||
assert folder_path.name == "hello"
|
||||
assert folder_path.exists()
|
||||
assert folder_path.parent == Path(temp_dir)
|
||||
|
||||
|
||||
def test_create_folder_structure_handles_complex_name_with_trailing_slash():
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
folder_path, folder_name, class_name = create_folder_structure("my-awesome_project/", parent_folder=temp_dir)
|
||||
|
||||
assert folder_name == "my_awesome_project"
|
||||
assert class_name == "MyAwesomeProject"
|
||||
assert folder_path.name == "my_awesome_project"
|
||||
assert folder_path.exists()
|
||||
assert folder_path.parent == Path(temp_dir)
|
||||
|
||||
|
||||
def test_create_folder_structure_normal_name_unchanged():
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
folder_path, folder_name, class_name = create_folder_structure("hello", parent_folder=temp_dir)
|
||||
|
||||
assert folder_name == "hello"
|
||||
assert class_name == "Hello"
|
||||
assert folder_path.name == "hello"
|
||||
assert folder_path.exists()
|
||||
assert folder_path.parent == Path(temp_dir)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def test_create_folder_structure_with_parent_folder():
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
parent_path = Path(temp_dir) / "parent"
|
||||
parent_path.mkdir()
|
||||
|
||||
folder_path, folder_name, class_name = create_folder_structure("child/", parent_folder=parent_path)
|
||||
|
||||
assert folder_name == "child"
|
||||
assert class_name == "Child"
|
||||
assert folder_path.name == "child"
|
||||
assert folder_path.parent == parent_path
|
||||
assert folder_path.exists()
|
||||
|
||||
|
||||
@mock.patch("crewai.cli.create_crew.copy_template")
|
||||
@mock.patch("crewai.cli.create_crew.write_env_file")
|
||||
@mock.patch("crewai.cli.create_crew.load_env_vars")
|
||||
def test_create_crew_with_trailing_slash_creates_valid_project(mock_load_env, mock_write_env, mock_copy_template, temp_dir):
|
||||
mock_load_env.return_value = {}
|
||||
|
||||
with tempfile.TemporaryDirectory() as work_dir:
|
||||
with mock.patch("crewai.cli.create_crew.create_folder_structure") as mock_create_folder:
|
||||
mock_folder_path = Path(work_dir) / "test_project"
|
||||
mock_create_folder.return_value = (mock_folder_path, "test_project", "TestProject")
|
||||
|
||||
create_crew("test-project/", skip_provider=True)
|
||||
|
||||
mock_create_folder.assert_called_once_with("test-project/", None)
|
||||
mock_copy_template.assert_called()
|
||||
copy_calls = mock_copy_template.call_args_list
|
||||
|
||||
for call in copy_calls:
|
||||
args = call[0]
|
||||
if len(args) >= 5:
|
||||
folder_name_arg = args[4]
|
||||
assert not folder_name_arg.endswith("/"), f"folder_name should not end with slash: {folder_name_arg}"
|
||||
|
||||
|
||||
@mock.patch("crewai.cli.create_crew.copy_template")
|
||||
@mock.patch("crewai.cli.create_crew.write_env_file")
|
||||
@mock.patch("crewai.cli.create_crew.load_env_vars")
|
||||
def test_create_crew_with_multiple_trailing_slashes(mock_load_env, mock_write_env, mock_copy_template, temp_dir):
|
||||
mock_load_env.return_value = {}
|
||||
|
||||
with tempfile.TemporaryDirectory() as work_dir:
|
||||
with mock.patch("crewai.cli.create_crew.create_folder_structure") as mock_create_folder:
|
||||
mock_folder_path = Path(work_dir) / "test_project"
|
||||
mock_create_folder.return_value = (mock_folder_path, "test_project", "TestProject")
|
||||
|
||||
create_crew("test-project///", skip_provider=True)
|
||||
|
||||
mock_create_folder.assert_called_once_with("test-project///", None)
|
||||
|
||||
|
||||
@mock.patch("crewai.cli.create_crew.copy_template")
|
||||
@mock.patch("crewai.cli.create_crew.write_env_file")
|
||||
@mock.patch("crewai.cli.create_crew.load_env_vars")
|
||||
def test_create_crew_normal_name_still_works(mock_load_env, mock_write_env, mock_copy_template, temp_dir):
|
||||
mock_load_env.return_value = {}
|
||||
|
||||
with tempfile.TemporaryDirectory() as work_dir:
|
||||
with mock.patch("crewai.cli.create_crew.create_folder_structure") as mock_create_folder:
|
||||
mock_folder_path = Path(work_dir) / "normal_project"
|
||||
mock_create_folder.return_value = (mock_folder_path, "normal_project", "NormalProject")
|
||||
|
||||
create_crew("normal-project", skip_provider=True)
|
||||
|
||||
mock_create_folder.assert_called_once_with("normal-project", None)
|
||||
|
||||
|
||||
def test_create_folder_structure_handles_spaces_and_dashes_with_slash():
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
folder_path, folder_name, class_name = create_folder_structure("My Cool-Project/", parent_folder=temp_dir)
|
||||
|
||||
assert folder_name == "my_cool_project"
|
||||
assert class_name == "MyCoolProject"
|
||||
assert folder_path.name == "my_cool_project"
|
||||
assert folder_path.exists()
|
||||
assert folder_path.parent == Path(temp_dir)
|
||||
|
||||
|
||||
def test_create_folder_structure_raises_error_for_invalid_names():
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
invalid_cases = [
|
||||
("123project/", "cannot start with a digit"),
|
||||
("True/", "reserved Python keyword"),
|
||||
("False/", "reserved Python keyword"),
|
||||
("None/", "reserved Python keyword"),
|
||||
("class/", "reserved Python keyword"),
|
||||
("def/", "reserved Python keyword"),
|
||||
(" /", "empty or contain only whitespace"),
|
||||
("", "empty or contain only whitespace"),
|
||||
("@#$/", "contains no valid characters"),
|
||||
]
|
||||
|
||||
for invalid_name, expected_error in invalid_cases:
|
||||
with pytest.raises(ValueError, match=expected_error):
|
||||
create_folder_structure(invalid_name, parent_folder=temp_dir)
|
||||
|
||||
|
||||
def test_create_folder_structure_validates_names():
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
valid_cases = [
|
||||
("hello/", "hello", "Hello"),
|
||||
("my-project/", "my_project", "MyProject"),
|
||||
("hello_world/", "hello_world", "HelloWorld"),
|
||||
("valid123/", "valid123", "Valid123"),
|
||||
("hello.world/", "helloworld", "HelloWorld"),
|
||||
("hello@world/", "helloworld", "HelloWorld"),
|
||||
]
|
||||
|
||||
for valid_name, expected_folder, expected_class in valid_cases:
|
||||
folder_path, folder_name, class_name = create_folder_structure(valid_name, parent_folder=temp_dir)
|
||||
assert folder_name == expected_folder
|
||||
assert class_name == expected_class
|
||||
|
||||
assert folder_name.isidentifier(), f"folder_name '{folder_name}' should be valid Python identifier"
|
||||
assert not keyword.iskeyword(folder_name), f"folder_name '{folder_name}' should not be Python keyword"
|
||||
assert not folder_name[0].isdigit(), f"folder_name '{folder_name}' should not start with digit"
|
||||
|
||||
assert class_name.isidentifier(), f"class_name '{class_name}' should be valid Python identifier"
|
||||
assert not keyword.iskeyword(class_name), f"class_name '{class_name}' should not be Python keyword"
|
||||
assert folder_path.parent == Path(temp_dir)
|
||||
|
||||
if folder_path.exists():
|
||||
shutil.rmtree(folder_path)
|
||||
|
||||
|
||||
@mock.patch("crewai.cli.create_crew.copy_template")
|
||||
@mock.patch("crewai.cli.create_crew.write_env_file")
|
||||
@mock.patch("crewai.cli.create_crew.load_env_vars")
|
||||
def test_create_crew_with_parent_folder_and_trailing_slash(mock_load_env, mock_write_env, mock_copy_template, temp_dir):
|
||||
mock_load_env.return_value = {}
|
||||
|
||||
with tempfile.TemporaryDirectory() as work_dir:
|
||||
parent_path = Path(work_dir) / "parent"
|
||||
parent_path.mkdir()
|
||||
|
||||
create_crew("child-crew/", skip_provider=True, parent_folder=parent_path)
|
||||
|
||||
crew_path = parent_path / "child_crew"
|
||||
assert crew_path.exists()
|
||||
assert not (crew_path / "src").exists()
|
||||
|
||||
|
||||
def test_create_folder_structure_folder_name_validation():
|
||||
"""Test that folder names are validated as valid Python module names"""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
folder_invalid_cases = [
|
||||
("123invalid/", "cannot start with a digit.*invalid Python module name"),
|
||||
("import/", "reserved Python keyword"),
|
||||
("class/", "reserved Python keyword"),
|
||||
("for/", "reserved Python keyword"),
|
||||
("@#$invalid/", "contains no valid characters.*Python module name"),
|
||||
]
|
||||
|
||||
for invalid_name, expected_error in folder_invalid_cases:
|
||||
with pytest.raises(ValueError, match=expected_error):
|
||||
create_folder_structure(invalid_name, parent_folder=temp_dir)
|
||||
|
||||
valid_cases = [
|
||||
("hello-world/", "hello_world"),
|
||||
("my.project/", "myproject"),
|
||||
("test@123/", "test123"),
|
||||
("valid_name/", "valid_name"),
|
||||
]
|
||||
|
||||
for valid_name, expected_folder in valid_cases:
|
||||
folder_path, folder_name, class_name = create_folder_structure(valid_name, parent_folder=temp_dir)
|
||||
assert folder_name == expected_folder
|
||||
assert folder_name.isidentifier()
|
||||
assert not keyword.iskeyword(folder_name)
|
||||
|
||||
if folder_path.exists():
|
||||
shutil.rmtree(folder_path)
|
||||
|
||||
@mock.patch("crewai.cli.create_crew.create_folder_structure")
|
||||
@mock.patch("crewai.cli.create_crew.copy_template")
|
||||
@mock.patch("crewai.cli.create_crew.load_env_vars")
|
||||
@mock.patch("crewai.cli.create_crew.get_provider_data")
|
||||
@mock.patch("crewai.cli.create_crew.select_provider")
|
||||
@mock.patch("crewai.cli.create_crew.select_model")
|
||||
@mock.patch("click.prompt")
|
||||
def test_env_vars_are_uppercased_in_env_file(
|
||||
mock_prompt,
|
||||
mock_select_model,
|
||||
mock_select_provider,
|
||||
mock_get_provider_data,
|
||||
mock_load_env_vars,
|
||||
mock_copy_template,
|
||||
mock_create_folder_structure,
|
||||
tmp_path
|
||||
):
|
||||
crew_path = tmp_path / "test_crew"
|
||||
crew_path.mkdir()
|
||||
mock_create_folder_structure.return_value = (crew_path, "test_crew", "TestCrew")
|
||||
|
||||
mock_load_env_vars.return_value = {}
|
||||
mock_get_provider_data.return_value = {"openai": ["gpt-4"]}
|
||||
mock_select_provider.return_value = "azure"
|
||||
mock_select_model.return_value = "azure/openai"
|
||||
mock_prompt.return_value = "fake-api-key"
|
||||
|
||||
create_crew("Test Crew")
|
||||
|
||||
env_file_path = crew_path / ".env"
|
||||
content = env_file_path.read_text()
|
||||
assert "MODEL=" in content
|
||||
@@ -1,17 +0,0 @@
|
||||
"""Test for version management."""
|
||||
|
||||
from crewai import __version__
|
||||
from crewai.cli.version import get_crewai_version
|
||||
|
||||
|
||||
def test_dynamic_versioning_consistency():
|
||||
"""Test that dynamic versioning provides consistent version across all access methods."""
|
||||
cli_version = get_crewai_version()
|
||||
package_version = __version__
|
||||
|
||||
# Both should return the same version string
|
||||
assert cli_version == package_version
|
||||
|
||||
# Version should not be empty
|
||||
assert package_version is not None
|
||||
assert len(package_version.strip()) > 0
|
||||
@@ -192,7 +192,7 @@ def test_lite_agent_structured_output():
|
||||
)
|
||||
|
||||
result = agent.kickoff(
|
||||
"What is the population of Tokyo? Return your structured output in JSON format with the following fields: summary, confidence",
|
||||
"What is the population of Tokyo? Return your strucutred output in JSON format with the following fields: summary, confidence",
|
||||
response_format=SimpleOutput,
|
||||
)
|
||||
|
||||
@@ -230,7 +230,7 @@ def test_lite_agent_returns_usage_metrics():
|
||||
)
|
||||
|
||||
result = agent.kickoff(
|
||||
"What is the population of Tokyo? Return your structured output in JSON format with the following fields: summary, confidence"
|
||||
"What is the population of Tokyo? Return your strucutred output in JSON format with the following fields: summary, confidence"
|
||||
)
|
||||
|
||||
assert result.usage_metrics is not None
|
||||
@@ -252,7 +252,7 @@ async def test_lite_agent_returns_usage_metrics_async():
|
||||
)
|
||||
|
||||
result = await agent.kickoff_async(
|
||||
"What is the population of Tokyo? Return your structured output in JSON format with the following fields: summary, confidence"
|
||||
"What is the population of Tokyo? Return your strucutred output in JSON format with the following fields: summary, confidence"
|
||||
)
|
||||
assert isinstance(result, LiteAgentOutput)
|
||||
assert "21 million" in result.raw or "37 million" in result.raw
|
||||
|
||||
Reference in New Issue
Block a user