fix: remove crewai dependency and add import guards in crewai components (#8923)

This commit is contained in:
Gabriel Luiz Freitas Almeida 2025-07-08 08:41:53 -03:00
commit 24db0cdc73
14 changed files with 716 additions and 951 deletions

View file

@ -104,11 +104,10 @@ dependencies = [
"sseclient-py==1.8.0",
"arize-phoenix-otel>=0.6.1",
"openinference-instrumentation-langchain>=0.1.29",
"crewai==0.102.0",
# "crewai>=0.126.0",
"mcp>=1.10.1",
"uv>=0.5.7",
"scipy>=1.14.1",
"ag2>=0.1.0",
"scrapegraph-py>=1.12.0",
"pydantic-ai>=0.0.19",
"smolagents>=1.8.0",

View file

@ -2,10 +2,6 @@ from collections.abc import Callable
from typing import Any, cast
import litellm
from crewai import LLM, Agent, Crew, Process, Task
from crewai.task import TaskOutput
from crewai.tools.base_tool import Tool
from langchain_core.agents import AgentAction, AgentFinish
from pydantic import SecretStr
from langflow.custom.custom_component.component import Component
@ -45,7 +41,7 @@ def _find_api_key(model):
return None
def convert_llm(llm: Any, excluded_keys=None) -> LLM:
def convert_llm(llm: Any, excluded_keys=None):
"""Converts a LangChain LLM object to a CrewAI-compatible LLM object.
Args:
@ -55,6 +51,12 @@ def convert_llm(llm: Any, excluded_keys=None) -> LLM:
Returns:
A CrewAI-compatible LLM object
"""
try:
from crewai import LLM
except ImportError as e:
msg = "CrewAI is not installed. Please install it with `uv pip install crewai`."
raise ImportError(msg) from e
if not llm:
return None
@ -109,6 +111,12 @@ def convert_tools(tools):
Returns:
A CrewAI-compatible tools list.
"""
try:
from crewai.tools.base_tool import Tool
except ImportError as e:
msg = "CrewAI is not installed. Please install it with `uv pip install crewai`."
raise ImportError(msg) from e
if not tools:
return []
@ -142,12 +150,12 @@ class BaseCrewComponent(Component):
]
# Model properties to exclude when creating a CrewAI LLM object
manager_llm: LLM | None
manager_llm = None
def task_is_valid(self, task_data: Data, crew_type: Process) -> Task:
def task_is_valid(self, task_data: Data, crew_type) -> bool:
return "task_type" in task_data and task_data.task_type == crew_type
def get_tasks_and_agents(self, agents_list=None) -> tuple[list[Task], list[Agent]]:
def get_tasks_and_agents(self, agents_list=None) -> tuple[list, list]:
# Allow passing a custom list of agents
if not agents_list:
agents_list = self.agents or []
@ -160,7 +168,7 @@ class BaseCrewComponent(Component):
return self.tasks, agents_list
def get_manager_llm(self) -> LLM | None:
def get_manager_llm(self):
if not self.manager_llm:
return None
@ -168,13 +176,19 @@ class BaseCrewComponent(Component):
return self.manager_llm
def build_crew(self) -> Crew:
def build_crew(self):
msg = "build_crew must be implemented in subclasses"
raise NotImplementedError(msg)
def get_task_callback(
self,
) -> Callable:
try:
from crewai.task import TaskOutput
except ImportError as e:
msg = "CrewAI is not installed. Please install it with `uv pip install crewai`."
raise ImportError(msg) from e
def task_callback(task_output: TaskOutput) -> None:
vertex_id = self._vertex.id if self._vertex else self.display_name or self.__class__.__name__
self.log(task_output.model_dump(), name=f"Task (Agent: {task_output.agent}) - {vertex_id}")
@ -184,7 +198,13 @@ class BaseCrewComponent(Component):
def get_step_callback(
self,
) -> Callable:
def step_callback(agent_output: AgentFinish | list[tuple[AgentAction, str]]) -> None:
try:
from langchain_core.agents import AgentFinish
except ImportError as e:
msg = "langchain_core is not installed. Please install it with `uv pip install langchain-core`."
raise ImportError(msg) from e
def step_callback(agent_output) -> None:
id_ = self._vertex.id if self._vertex else self.display_name
if isinstance(agent_output, AgentFinish):
messages = agent_output.messages

View file

@ -1,4 +1,7 @@
from crewai import Task
try:
from crewai import Task
except ImportError:
Task = object
class SequentialTask(Task):

View file

@ -1,5 +1,3 @@
from crewai import Agent
from langflow.base.agents.crewai.crew import convert_llm, convert_tools
from langflow.custom.custom_component.component import Component
from langflow.io import BoolInput, DictInput, HandleInput, MultilineInput, Output
@ -22,6 +20,7 @@ class CrewAIAgentComponent(Component):
description = "Represents an agent of CrewAI."
documentation: str = "https://docs.crewai.com/how-to/LLM-Connections/"
icon = "CrewAI"
legacy = True
inputs = [
MultilineInput(name="role", display_name="Role", info="The role of the agent."),
@ -80,7 +79,13 @@ class CrewAIAgentComponent(Component):
Output(display_name="Agent", name="output", method="build_output"),
]
def build_output(self) -> Agent:
def build_output(self):
try:
from crewai import Agent
except ImportError as e:
msg = "CrewAI is not installed. Please install it with `uv pip install crewai`."
raise ImportError(msg) from e
kwargs = self.kwargs or {}
# Define the Agent

View file

@ -1,5 +1,3 @@
from crewai import Crew, Process
from langflow.base.agents.crewai.crew import BaseCrewComponent
from langflow.io import HandleInput
@ -11,6 +9,7 @@ class HierarchicalCrewComponent(BaseCrewComponent):
)
documentation: str = "https://docs.crewai.com/how-to/Hierarchical/"
icon = "CrewAI"
legacy = True
inputs = [
*BaseCrewComponent._base_inputs,
@ -20,7 +19,13 @@ class HierarchicalCrewComponent(BaseCrewComponent):
HandleInput(name="manager_agent", display_name="Manager Agent", input_types=["Agent"], required=False),
]
def build_crew(self) -> Crew:
def build_crew(self):
try:
from crewai import Crew, Process
except ImportError as e:
msg = "CrewAI is not installed. Please install it with `uv pip install crewai`."
raise ImportError(msg) from e
tasks, agents = self.get_tasks_and_agents()
manager_llm = self.get_manager_llm()

View file

@ -7,6 +7,7 @@ class HierarchicalTaskComponent(Component):
display_name: str = "Hierarchical Task"
description: str = "Each task must have a description, an expected output and an agent responsible for execution."
icon = "CrewAI"
legacy = True
inputs = [
MultilineInput(
name="task_description",

View file

@ -1,5 +1,3 @@
from crewai import Agent, Crew, Process, Task
from langflow.base.agents.crewai.crew import BaseCrewComponent
from langflow.io import HandleInput
from langflow.schema.message import Message
@ -10,6 +8,7 @@ class SequentialCrewComponent(BaseCrewComponent):
description: str = "Represents a group of agents with tasks that are executed sequentially."
documentation: str = "https://docs.crewai.com/how-to/Sequential/"
icon = "CrewAI"
legacy = True
inputs = [
*BaseCrewComponent._base_inputs,
@ -17,11 +16,11 @@ class SequentialCrewComponent(BaseCrewComponent):
]
@property
def agents(self: "SequentialCrewComponent") -> list[Agent]:
def agents(self: "SequentialCrewComponent") -> list:
# Derive agents directly from linked tasks
return [task.agent for task in self.tasks if hasattr(task, "agent")]
def get_tasks_and_agents(self, agents_list=None) -> tuple[list[Task], list[Agent]]:
def get_tasks_and_agents(self, agents_list=None) -> tuple[list, list]:
# Use the agents property to derive agents
if not agents_list:
existing_agents = self.agents
@ -30,6 +29,12 @@ class SequentialCrewComponent(BaseCrewComponent):
return super().get_tasks_and_agents(agents_list=agents_list)
def build_crew(self) -> Message:
try:
from crewai import Crew, Process
except ImportError as e:
msg = "CrewAI is not installed. Please install it with `uv pip install crewai`."
raise ImportError(msg) from e
tasks, agents = self.get_tasks_and_agents()
return Crew(

View file

@ -7,6 +7,7 @@ class SequentialTaskComponent(Component):
display_name: str = "Sequential Task"
description: str = "Each task must have a description, an expected output and an agent responsible for execution."
icon = "CrewAI"
legacy = True
inputs = [
MultilineInput(
name="task_description",
@ -65,7 +66,7 @@ class SequentialTaskComponent(Component):
tasks.append(task)
self.status = task
if self.task:
if isinstance(self.task, list) and all(isinstance(task, SequentialTask) for task in self.task):
if isinstance(self.task, list) and all(isinstance(task_item, SequentialTask) for task_item in self.task):
tasks = self.task + tasks
elif isinstance(self.task, SequentialTask):
tasks = [self.task, *tasks]

View file

@ -1,5 +1,3 @@
from crewai import Agent, Task
from langflow.base.agents.crewai.tasks import SequentialTask
from langflow.custom.custom_component.component import Component
from langflow.io import BoolInput, DictInput, HandleInput, MultilineInput, Output
@ -10,6 +8,7 @@ class SequentialTaskAgentComponent(Component):
description = "Creates a CrewAI Task and its associated Agent."
documentation = "https://docs.crewai.com/how-to/LLM-Connections/"
icon = "CrewAI"
legacy = True
inputs = [
# Agent inputs
@ -105,6 +104,12 @@ class SequentialTaskAgentComponent(Component):
]
def build_agent_and_task(self) -> list[SequentialTask]:
try:
from crewai import Agent, Task
except ImportError as e:
msg = "CrewAI is not installed. Please install it with `uv pip install crewai`."
raise ImportError(msg) from e
# Build the agent
agent_kwargs = self.agent_kwargs or {}
agent = Agent(

View file

@ -900,7 +900,9 @@ class Component(CustomComponent):
def _get_method_return_type(self, method_name: str) -> list[str]:
method = getattr(self, method_name)
return_type = get_type_hints(method)["return"]
return_type = get_type_hints(method).get("return")
if return_type is None:
return []
extracted_return_types = self._extract_return_type(return_type)
return [format_type(extracted_return_type) for extracted_return_type in extracted_return_types]

View file

@ -2,9 +2,7 @@ from .starter_projects import (
basic_prompting_graph,
blog_writer_graph,
document_qa_graph,
hierarchical_tasks_agent_graph,
memory_chatbot_graph,
sequential_tasks_agent_graph,
vector_store_rag_graph,
)
@ -16,8 +14,6 @@ def get_starter_projects_graphs():
document_qa_graph(),
memory_chatbot_graph(),
vector_store_rag_graph(),
sequential_tasks_agent_graph(),
hierarchical_tasks_agent_graph(),
]

View file

@ -6,5 +6,5 @@ async def test_get_starter_projects(client: AsyncClient, logged_in_headers):
response = await client.get("api/v1/starter-projects/", headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
assert response.status_code == status.HTTP_200_OK, response.text
assert isinstance(result, list), "The result must be a list"

View file

@ -9,6 +9,14 @@ from langflow.schema import dotdict
from langflow.template import Output
from typing_extensions import override
crewai_available = False
try:
import crewai # noqa: F401
crewai_available = True
except ImportError:
pass
def test_set_invalid_output():
chatinput = ChatInput()
@ -17,6 +25,7 @@ def test_set_invalid_output():
chatoutput.set(input_value=chatinput.build_config)
@pytest.mark.skipif(not crewai_available, reason="CrewAI is not installed")
def test_set_component():
crewai_agent = CrewAIAgentComponent()
task = SequentialTaskComponent()

1550
uv.lock generated

File diff suppressed because it is too large Load diff