Compare commits

...

6 Commits

Author SHA1 Message Date
Devin AI
98221eb266 Fix import sorting issues to resolve CI linting failures
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-03-17 20:27:56 +00:00
Devin AI
bca79fa485 Implement PR review suggestions: improve type hints, documentation, and tests
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-03-17 20:24:15 +00:00
Devin AI
0d0da0a22d Fix import sorting in test file
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-03-17 20:16:11 +00:00
Devin AI
80f0011743 Fix issue #2392: Preserve ConditionalTask type in Crew.copy() and kickoff_for_each()
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-03-17 20:13:59 +00:00
João Moura
e723e5ca3f preparign new version 2025-03-17 09:13:21 -07:00
Jakub Kopecký
24f1a19310 feat: add docs for ApifyActorsTool (#2254)
* add docs for ApifyActorsTool

* improve readme, add link to template

* format

* improve tool docs

* improve readme

* Update apifyactorstool.mdx (#1)

* Update apifyactorstool.mdx

* Update apifyactorstool.mdx

* dans suggestions

* custom apify icon

* update descripton

* Update apifyactorstool.mdx

---------

Co-authored-by: Jan Čurn <jan.curn@gmail.com>
Co-authored-by: Brandon Hancock (bhancock_ai) <109994880+bhancockio@users.noreply.github.com>
2025-03-16 12:29:57 -04:00
12 changed files with 236 additions and 12 deletions

View File

@@ -106,6 +106,7 @@ Here is a list of the available tools and their descriptions:
| Tool | Description |
| :------------------------------- | :--------------------------------------------------------------------------------------------- |
| **ApifyActorsTool** | A tool that integrates Apify Actors with your workflows for web scraping and automation tasks. |
| **BrowserbaseLoadTool** | A tool for interacting with and extracting data from web browsers. |
| **CodeDocsSearchTool** | A RAG tool optimized for searching through code documentation and related technical documents. |
| **CodeInterpreterTool** | A tool for interpreting python code. |

View File

@@ -154,6 +154,7 @@
"group": "Tools",
"pages": [
"tools/aimindtool",
"tools/apifyactorstool",
"tools/bravesearchtool",
"tools/browserbaseloadtool",
"tools/codedocssearchtool",
@@ -220,4 +221,4 @@
"linkedin": "https://www.linkedin.com/company/crewai-inc",
"youtube": "https://youtube.com/@crewAIInc"
}
}
}

View File

@@ -0,0 +1,99 @@
---
title: Apify Actors
description: "`ApifyActorsTool` lets you call Apify Actors to provide your CrewAI workflows with web scraping, crawling, data extraction, and web automation capabilities."
# hack to use custom Apify icon
icon: "); -webkit-mask-image: url('https://upload.wikimedia.org/wikipedia/commons/a/ae/Apify.svg');/*"
---
# `ApifyActorsTool`
Integrate [Apify Actors](https://apify.com/actors) into your CrewAI workflows.
## Description
The `ApifyActorsTool` connects [Apify Actors](https://apify.com/actors), cloud-based programs for web scraping and automation, to your CrewAI workflows.
Use any of the 4,000+ Actors on [Apify Store](https://apify.com/store) for use cases such as extracting data from social media, search engines, online maps, e-commerce sites, travel portals, or general websites.
For details, see the [Apify CrewAI integration](https://docs.apify.com/platform/integrations/crewai) in Apify documentation.
## Steps to get started
<Steps>
<Step title="Install dependencies">
Install `crewai[tools]` and `langchain-apify` using pip: `pip install 'crewai[tools]' langchain-apify`.
</Step>
<Step title="Obtain an Apify API token">
Sign up to [Apify Console](https://console.apify.com/) and get your [Apify API token](https://console.apify.com/settings/integrations)..
</Step>
<Step title="Configure environment">
Set your Apify API token as the `APIFY_API_TOKEN` environment variable to enable the tool's functionality.
</Step>
</Steps>
## Usage example
Use the `ApifyActorsTool` manually to run the [RAG Web Browser Actor](https://apify.com/apify/rag-web-browser) to perform a web search:
```python
from crewai_tools import ApifyActorsTool
# Initialize the tool with an Apify Actor
tool = ApifyActorsTool(actor_name="apify/rag-web-browser")
# Run the tool with input parameters
results = tool.run(run_input={"query": "What is CrewAI?", "maxResults": 5})
# Process the results
for result in results:
print(f"URL: {result['metadata']['url']}")
print(f"Content: {result.get('markdown', 'N/A')[:100]}...")
```
### Expected output
Here is the output from running the code above:
```text
URL: https://www.example.com/crewai-intro
Content: CrewAI is a framework for building AI-powered workflows...
URL: https://docs.crewai.com/
Content: Official documentation for CrewAI...
```
The `ApifyActorsTool` automatically fetches the Actor definition and input schema from Apify using the provided `actor_name` and then constructs the tool description and argument schema. This means you need to specify only a valid `actor_name`, and the tool handles the rest when used with agents—no need to specify the `run_input`. Here's how it works:
```python
from crewai import Agent
from crewai_tools import ApifyActorsTool
rag_browser = ApifyActorsTool(actor_name="apify/rag-web-browser")
agent = Agent(
role="Research Analyst",
goal="Find and summarize information about specific topics",
backstory="You are an experienced researcher with attention to detail",
tools=[rag_browser],
)
```
You can run other Actors from [Apify Store](https://apify.com/store) simply by changing the `actor_name` and, when using it manually, adjusting the `run_input` based on the Actor input schema.
For an example of usage with agents, see the [CrewAI Actor template](https://apify.com/templates/python-crewai).
## Configuration
The `ApifyActorsTool` requires these inputs to work:
- **`actor_name`**
The ID of the Apify Actor to run, e.g., `"apify/rag-web-browser"`. Browse all Actors on [Apify Store](https://apify.com/store).
- **`run_input`**
A dictionary of input parameters for the Actor when running the tool manually.
- For example, for the `apify/rag-web-browser` Actor: `{"query": "search term", "maxResults": 5}`
- See the Actor's [input schema](https://apify.com/apify/rag-web-browser/input-schema) for the list of input parameters.
## Resources
- **[Apify](https://apify.com/)**: Explore the Apify platform.
- **[How to build an AI agent on Apify](https://blog.apify.com/how-to-build-an-ai-agent/)** - A complete step-by-step guide to creating, publishing, and monetizing AI agents on the Apify platform.
- **[RAG Web Browser Actor](https://apify.com/apify/rag-web-browser)**: A popular Actor for web search for LLMs.
- **[CrewAI Integration Guide](https://docs.apify.com/platform/integrations/crewai)**: Follow the official guide for integrating Apify and CrewAI.

View File

@@ -1,6 +1,6 @@
[project]
name = "crewai"
version = "0.105.0"
version = "0.108.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.13"

View File

@@ -14,7 +14,7 @@ warnings.filterwarnings(
category=UserWarning,
module="pydantic.main",
)
__version__ = "0.105.0"
__version__ = "0.108.0"
__all__ = [
"Agent",
"Crew",

View File

@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<3.13"
dependencies = [
"crewai[tools]>=0.105.0,<1.0.0"
"crewai[tools]>=0.108.0,<1.0.0"
]
[project.scripts]

View File

@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<3.13"
dependencies = [
"crewai[tools]>=0.105.0,<1.0.0",
"crewai[tools]>=0.108.0,<1.0.0",
]
[project.scripts]

View File

@@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}"
readme = "README.md"
requires-python = ">=3.10,<3.13"
dependencies = [
"crewai[tools]>=0.105.0"
"crewai[tools]>=0.108.0"
]
[tool.crewai]

View File

@@ -9,6 +9,7 @@ from copy import copy
from hashlib import md5
from pathlib import Path
from typing import (
TYPE_CHECKING,
Any,
Callable,
ClassVar,
@@ -33,6 +34,9 @@ from pydantic_core import PydanticCustomError
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.security import Fingerprint, SecurityConfig
if TYPE_CHECKING:
from crewai.tasks.conditional_task import ConditionalTask
from crewai.tasks.guardrail_result import GuardrailResult
from crewai.tasks.output_format import OutputFormat
from crewai.tasks.task_output import TaskOutput
@@ -617,8 +621,17 @@ class Task(BaseModel):
def copy(
self, agents: List["BaseAgent"], task_mapping: Dict[str, "Task"]
) -> "Task":
"""Create a deep copy of the Task."""
) -> Union["Task", "ConditionalTask"]:
"""
Creates a deep copy of the task while preserving its specific type (Task or ConditionalTask).
Args:
agents: List of agents to search for the agent by role
task_mapping: Dictionary mapping task keys to tasks
Returns:
Union[Task, ConditionalTask]: A copy of the task maintaining its original type.
"""
exclude = {
"id",
"agent",
@@ -641,7 +654,9 @@ class Task(BaseModel):
cloned_agent = get_agent_by_role(self.agent.role) if self.agent else None
cloned_tools = copy(self.tools) if self.tools else []
copied_task = Task(
# Use the actual class of the instance being copied, not just Task
task_class = self.__class__
copied_task = task_class(
**copied_data,
context=cloned_context,
agent=cloned_agent,

View File

@@ -1,8 +1,12 @@
from typing import Any, Callable
from typing import TYPE_CHECKING, Any, Callable
from pydantic import Field
from crewai.task import Task
if TYPE_CHECKING:
from crewai.task import Task
else:
# Import the base class at runtime
from crewai.task import Task
from crewai.tasks.output_format import OutputFormat
from crewai.tasks.task_output import TaskOutput

View File

@@ -0,0 +1,104 @@
import pytest
from crewai import Agent, Crew, Task
from crewai.tasks.conditional_task import ConditionalTask
from crewai.tasks.task_output import TaskOutput
@pytest.fixture
def test_agent():
"""Fixture for creating a test agent."""
return Agent(
role="Researcher",
goal="Research topics",
backstory="You are a researcher."
)
@pytest.fixture
def test_task(test_agent):
"""Fixture for creating a regular task."""
return Task(
description="Research topic A",
expected_output="Research results for topic A",
agent=test_agent
)
@pytest.fixture
def test_conditional_task(test_agent):
"""Fixture for creating a conditional task."""
return ConditionalTask(
description="Research topic B if topic A was successful",
expected_output="Research results for topic B",
agent=test_agent,
condition=lambda output: "success" in output.raw.lower()
)
@pytest.fixture
def test_crew(test_agent, test_task, test_conditional_task):
"""Fixture for creating a crew with both regular and conditional tasks."""
return Crew(
agents=[test_agent],
tasks=[test_task, test_conditional_task]
)
def test_conditional_task_preserved_in_copy(test_crew):
"""Test that ConditionalTask objects are preserved when copying a Crew."""
# Create a copy of the crew
crew_copy = test_crew.copy()
# Check that the conditional task is still a ConditionalTask in the copied crew
assert isinstance(crew_copy.tasks[1], ConditionalTask)
assert hasattr(crew_copy.tasks[1], "should_execute")
def test_conditional_task_preserved_in_kickoff_for_each(test_crew, test_agent):
"""Test that ConditionalTask objects are preserved when using kickoff_for_each."""
from unittest.mock import patch
# Mock the kickoff method to avoid actual execution
with patch.object(Crew, "kickoff") as mock_kickoff:
# Set up the mock to return a TaskOutput
mock_output = TaskOutput(
description="Mock task output",
raw="Success with topic",
agent=test_agent.role
)
mock_kickoff.return_value = mock_output
# Call kickoff_for_each with test inputs
inputs = [{"topic": "test1"}, {"topic": "test2"}]
test_crew.kickoff_for_each(inputs=inputs)
# Verify the mock was called with the expected inputs
assert mock_kickoff.call_count == len(inputs)
# Create a copy of the crew to verify the type preservation
# (since we can't directly access the crews created inside kickoff_for_each)
crew_copy = test_crew.copy()
assert isinstance(crew_copy.tasks[1], ConditionalTask)
def test_conditional_task_copy_with_none_values(test_agent, test_task):
"""Test that ConditionalTask objects are preserved when copying with optional fields."""
# Create a conditional task with optional fields
conditional_task = ConditionalTask(
description="Research topic B if topic A was successful",
expected_output="Research results for topic B", # Required field
agent=test_agent,
condition=lambda output: "success" in output.raw.lower(),
context=None # Optional field that can be None
)
# Create a crew with both a regular task and the conditional task
crew = Crew(
agents=[test_agent],
tasks=[test_task, conditional_task]
)
# Create a copy of the crew
crew_copy = crew.copy()
# Check that the conditional task is still a ConditionalTask in the copied crew
assert isinstance(crew_copy.tasks[1], ConditionalTask)
assert hasattr(crew_copy.tasks[1], "should_execute")
assert crew_copy.tasks[1].context is None # Verify None value is preserved

2
uv.lock generated
View File

@@ -619,7 +619,7 @@ wheels = [
[[package]]
name = "crewai"
version = "0.105.0"
version = "0.108.0"
source = { editable = "." }
dependencies = [
{ name = "appdirs" },