diff --git a/pyproject.toml b/pyproject.toml index ef8181cbc..b9e6bc986 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "crewai" -version = "0.76.2" +version = "0.76.9" 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" @@ -16,7 +16,7 @@ dependencies = [ "opentelemetry-exporter-otlp-proto-http>=1.22.0", "instructor>=1.3.3", "regex>=2024.9.11", - "crewai-tools>=0.13.2", + "crewai-tools>=0.13.4", "click>=8.1.7", "python-dotenv>=1.0.0", "appdirs>=1.4.4", @@ -37,7 +37,7 @@ Documentation = "https://docs.crewai.com" Repository = "https://github.com/crewAIInc/crewAI" [project.optional-dependencies] -tools = ["crewai-tools>=0.13.2"] +tools = ["crewai-tools>=0.13.4"] agentops = ["agentops>=0.3.0"] [tool.uv] @@ -52,7 +52,7 @@ dev-dependencies = [ "mkdocs-material-extensions>=1.3.1", "pillow>=10.2.0", "cairosvg>=2.7.1", - "crewai-tools>=0.13.2", + "crewai-tools>=0.13.4", "pytest>=8.0.0", "pytest-vcr>=1.0.2", "python-dotenv>=1.0.0", diff --git a/src/crewai/__init__.py b/src/crewai/__init__.py index 5160aa77f..0a2f02a59 100644 --- a/src/crewai/__init__.py +++ b/src/crewai/__init__.py @@ -14,5 +14,5 @@ warnings.filterwarnings( category=UserWarning, module="pydantic.main", ) -__version__ = "0.76.2" +__version__ = "0.76.9" __all__ = ["Agent", "Crew", "Process", "Task", "Pipeline", "Router", "LLM", "Flow"] diff --git a/src/crewai/cli/templates/crew/pyproject.toml b/src/crewai/cli/templates/crew/pyproject.toml index ed32aa600..447cfcb86 100644 --- a/src/crewai/cli/templates/crew/pyproject.toml +++ b/src/crewai/cli/templates/crew/pyproject.toml @@ -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.76.2,<1.0.0" + "crewai[tools]>=0.76.9,<1.0.0" ] [project.scripts] diff --git a/src/crewai/cli/templates/flow/pyproject.toml b/src/crewai/cli/templates/flow/pyproject.toml index 815e8b205..1ef8f7b36 100644 --- a/src/crewai/cli/templates/flow/pyproject.toml +++ b/src/crewai/cli/templates/flow/pyproject.toml @@ -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.76.2,<1.0.0", + "crewai[tools]>=0.76.9,<1.0.0", ] [project.scripts] diff --git a/src/crewai/cli/templates/pipeline/pyproject.toml b/src/crewai/cli/templates/pipeline/pyproject.toml index b4e74abea..53f304283 100644 --- a/src/crewai/cli/templates/pipeline/pyproject.toml +++ b/src/crewai/cli/templates/pipeline/pyproject.toml @@ -6,7 +6,7 @@ authors = ["Your Name "] [tool.poetry.dependencies] python = ">=3.10,<=3.13" -crewai = { extras = ["tools"], version = ">=0.76.2,<1.0.0" } +crewai = { extras = ["tools"], version = ">=0.76.9,<1.0.0" } asyncio = "*" [tool.poetry.scripts] diff --git a/src/crewai/cli/templates/pipeline_router/pyproject.toml b/src/crewai/cli/templates/pipeline_router/pyproject.toml index bd487ce95..33d5c58af 100644 --- a/src/crewai/cli/templates/pipeline_router/pyproject.toml +++ b/src/crewai/cli/templates/pipeline_router/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = ["Your Name "] requires-python = ">=3.10,<=3.13" dependencies = [ - "crewai[tools]>=0.76.2,<1.0.0" + "crewai[tools]>=0.76.9,<1.0.0" ] [project.scripts] diff --git a/src/crewai/cli/templates/tool/pyproject.toml b/src/crewai/cli/templates/tool/pyproject.toml index 360a940ed..849298b6b 100644 --- a/src/crewai/cli/templates/tool/pyproject.toml +++ b/src/crewai/cli/templates/tool/pyproject.toml @@ -5,6 +5,6 @@ description = "Power up your crews with {{folder_name}}" readme = "README.md" requires-python = ">=3.10,<=3.13" dependencies = [ - "crewai[tools]>=0.76.2" + "crewai[tools]>=0.76.9" ] diff --git a/src/crewai/flow/flow.py b/src/crewai/flow/flow.py index de9b2eeb2..e7231e13f 100644 --- a/src/crewai/flow/flow.py +++ b/src/crewai/flow/flow.py @@ -1,5 +1,3 @@ -# flow.py - import asyncio import inspect from typing import Any, Callable, Dict, Generic, List, Set, Type, TypeVar, Union @@ -120,6 +118,8 @@ class FlowMeta(type): methods = attr_value.__trigger_methods__ condition_type = getattr(attr_value, "__condition_type__", "OR") listeners[attr_name] = (condition_type, methods) + + # TODO: should we add a check for __condition_type__ 'AND'? elif hasattr(attr_value, "__is_router__"): routers[attr_value.__router_for__] = attr_name possible_returns = get_possible_return_constants(attr_value) @@ -159,7 +159,8 @@ class Flow(Generic[T], metaclass=FlowMeta): def __init__(self) -> None: self._methods: Dict[str, Callable] = {} self._state: T = self._create_initial_state() - self._completed_methods: Set[str] = set() + self._executed_methods: Set[str] = set() + self._scheduled_tasks: Set[str] = set() self._pending_and_listeners: Dict[str, Set[str]] = {} self._method_outputs: List[Any] = [] # List to store all method outputs @@ -216,17 +217,24 @@ class Flow(Generic[T], metaclass=FlowMeta): else: return None # Or raise an exception if no methods were executed - async def _execute_start_method(self, start_method: str) -> None: - result = await self._execute_method(self._methods[start_method]) - await self._execute_listeners(start_method, result) + async def _execute_start_method(self, start_method_name: str) -> None: + result = await self._execute_method( + start_method_name, self._methods[start_method_name] + ) + await self._execute_listeners(start_method_name, result) - async def _execute_method(self, method: Callable, *args: Any, **kwargs: Any) -> Any: + async def _execute_method( + self, method_name: str, method: Callable, *args: Any, **kwargs: Any + ) -> Any: result = ( await method(*args, **kwargs) if asyncio.iscoroutinefunction(method) else method(*args, **kwargs) ) self._method_outputs.append(result) # Store the output + + self._executed_methods.add(method_name) + return result async def _execute_listeners(self, trigger_method: str, result: Any) -> None: @@ -234,32 +242,40 @@ class Flow(Generic[T], metaclass=FlowMeta): if trigger_method in self._routers: router_method = self._methods[self._routers[trigger_method]] - path = await self._execute_method(router_method) + path = await self._execute_method( + trigger_method, router_method + ) # TODO: Change or not? # Use the path as the new trigger method trigger_method = path - for listener, (condition_type, methods) in self._listeners.items(): + for listener_name, (condition_type, methods) in self._listeners.items(): if condition_type == "OR": if trigger_method in methods: - listener_tasks.append( - self._execute_single_listener(listener, result) - ) + if ( + listener_name not in self._executed_methods + and listener_name not in self._scheduled_tasks + ): + self._scheduled_tasks.add(listener_name) + listener_tasks.append( + self._execute_single_listener(listener_name, result) + ) elif condition_type == "AND": - if listener not in self._pending_and_listeners: - self._pending_and_listeners[listener] = set() - self._pending_and_listeners[listener].add(trigger_method) - if set(methods) == self._pending_and_listeners[listener]: - listener_tasks.append( - self._execute_single_listener(listener, result) - ) - del self._pending_and_listeners[listener] + if all(method in self._executed_methods for method in methods): + if ( + listener_name not in self._executed_methods + and listener_name not in self._scheduled_tasks + ): + self._scheduled_tasks.add(listener_name) + listener_tasks.append( + self._execute_single_listener(listener_name, result) + ) # Run all listener tasks concurrently and wait for them to complete await asyncio.gather(*listener_tasks) - async def _execute_single_listener(self, listener: str, result: Any) -> None: + async def _execute_single_listener(self, listener_name: str, result: Any) -> None: try: - method = self._methods[listener] + method = self._methods[listener_name] sig = inspect.signature(method) params = list(sig.parameters.values()) @@ -268,15 +284,22 @@ class Flow(Generic[T], metaclass=FlowMeta): if method_params: # If listener expects parameters, pass the result - listener_result = await self._execute_method(method, result) + listener_result = await self._execute_method( + listener_name, method, result + ) else: # If listener does not expect parameters, call without arguments - listener_result = await self._execute_method(method) + listener_result = await self._execute_method(listener_name, method) + + # Remove from scheduled tasks after execution + self._scheduled_tasks.discard(listener_name) # Execute listeners of this listener - await self._execute_listeners(listener, listener_result) + await self._execute_listeners(listener_name, listener_result) except Exception as e: - print(f"[Flow._execute_single_listener] Error in method {listener}: {e}") + print( + f"[Flow._execute_single_listener] Error in method {listener_name}: {e}" + ) import traceback traceback.print_exc() diff --git a/uv.lock b/uv.lock index df2572402..f7f6a1839 100644 --- a/uv.lock +++ b/uv.lock @@ -604,7 +604,7 @@ wheels = [ [[package]] name = "crewai" -version = "0.76.2" +version = "0.76.9" source = { editable = "." } dependencies = [ { name = "appdirs" }, @@ -665,8 +665,8 @@ requires-dist = [ { name = "auth0-python", specifier = ">=4.7.1" }, { name = "chromadb", specifier = ">=0.4.24" }, { name = "click", specifier = ">=8.1.7" }, - { name = "crewai-tools", specifier = ">=0.13.2" }, - { name = "crewai-tools", marker = "extra == 'tools'", specifier = ">=0.13.2" }, + { name = "crewai-tools", specifier = ">=0.13.4" }, + { name = "crewai-tools", marker = "extra == 'tools'", specifier = ">=0.13.4" }, { name = "instructor", specifier = ">=1.3.3" }, { name = "json-repair", specifier = ">=0.25.2" }, { name = "jsonref", specifier = ">=1.1.0" }, @@ -688,7 +688,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "cairosvg", specifier = ">=2.7.1" }, - { name = "crewai-tools", specifier = ">=0.13.2" }, + { name = "crewai-tools", specifier = ">=0.13.4" }, { name = "mkdocs", specifier = ">=1.4.3" }, { name = "mkdocs-material", specifier = ">=9.5.7" }, { name = "mkdocs-material-extensions", specifier = ">=1.3.1" }, @@ -707,7 +707,7 @@ dev = [ [[package]] name = "crewai-tools" -version = "0.13.2" +version = "0.13.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, @@ -725,9 +725,9 @@ dependencies = [ { name = "requests" }, { name = "selenium" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/02/136f42ed8a7bd706a85663714c615bdcb684e43e95e4719c892aa0ce3d53/crewai_tools-0.13.2.tar.gz", hash = "sha256:c6782f2e868c0e96b25891f1b40fb8c90c01e920bab2fd1388f89ef1d7a4b99b", size = 816250 } +sdist = { url = "https://files.pythonhosted.org/packages/64/bd/eff7b633a0b28ff4ed115adde1499e3dcc683e4f0b5c378a4c6f5c0c1bf6/crewai_tools-0.13.4.tar.gz", hash = "sha256:b6ac527633b7018471d892c21ac96bc961a86b6626d996b1ed7d53cd481d4505", size = 816588 } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/30/df215173b6193b2cfb1902a339443be73056eae89579805b853c6f359761/crewai_tools-0.13.2-py3-none-any.whl", hash = "sha256:8c7583c9559fb625f594349c6553a5251ebd7b21918735ad6fbe8bab7ec3db50", size = 463444 }, + { url = "https://files.pythonhosted.org/packages/6c/40/93cd347d854059cf5e54a81b70f896deea7ad1f03e9c024549eb323c4da5/crewai_tools-0.13.4-py3-none-any.whl", hash = "sha256:eda78fe3c4df57676259d8dd6b2610fa31f89b90909512f15893adb57fb9e825", size = 463703 }, ] [[package]]