mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-09 08:08:32 +00:00
Everything is working
This commit is contained in:
@@ -1,12 +1,85 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
from typing import Any, Callable, Dict, Generic, List, Type, TypeVar, Union
|
from typing import Any, Callable, Dict, Generic, List, Set, Type, TypeVar, Union
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
T = TypeVar("T", bound=Union[BaseModel, Dict[str, Any]])
|
T = TypeVar("T", bound=Union[BaseModel, Dict[str, Any]])
|
||||||
|
|
||||||
|
|
||||||
|
def start():
|
||||||
|
def decorator(func):
|
||||||
|
print(f"[start decorator] Decorating start method: {func.__name__}")
|
||||||
|
func.__is_start_method__ = True
|
||||||
|
return func
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def listen(condition):
|
||||||
|
def decorator(func):
|
||||||
|
print(
|
||||||
|
f"[listen decorator] Decorating listener: {func.__name__} with condition: {condition}"
|
||||||
|
)
|
||||||
|
if isinstance(condition, str):
|
||||||
|
func.__trigger_methods__ = [condition]
|
||||||
|
func.__condition_type__ = "OR"
|
||||||
|
print(
|
||||||
|
f"[listen decorator] Set __trigger_methods__ for {func.__name__}: [{condition}] with mode: OR"
|
||||||
|
)
|
||||||
|
elif (
|
||||||
|
isinstance(condition, dict)
|
||||||
|
and "type" in condition
|
||||||
|
and "methods" in condition
|
||||||
|
):
|
||||||
|
func.__trigger_methods__ = condition["methods"]
|
||||||
|
func.__condition_type__ = condition["type"]
|
||||||
|
print(
|
||||||
|
f"[listen decorator] Set __trigger_methods__ for {func.__name__}: {func.__trigger_methods__} with mode: {func.__condition_type__}"
|
||||||
|
)
|
||||||
|
elif callable(condition) and hasattr(condition, "__name__"):
|
||||||
|
func.__trigger_methods__ = [condition.__name__]
|
||||||
|
func.__condition_type__ = "OR"
|
||||||
|
print(
|
||||||
|
f"[listen decorator] Set __trigger_methods__ for {func.__name__}: [{condition.__name__}] with mode: OR"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
"Condition must be a method, string, or a result of or_() or and_()"
|
||||||
|
)
|
||||||
|
return func
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def or_(*conditions):
|
||||||
|
methods = []
|
||||||
|
for condition in conditions:
|
||||||
|
if isinstance(condition, dict) and "methods" in condition:
|
||||||
|
methods.extend(condition["methods"])
|
||||||
|
elif callable(condition) and hasattr(condition, "__name__"):
|
||||||
|
methods.append(condition.__name__)
|
||||||
|
elif isinstance(condition, str):
|
||||||
|
methods.append(condition)
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid condition in or_()")
|
||||||
|
return {"type": "OR", "methods": methods}
|
||||||
|
|
||||||
|
|
||||||
|
def and_(*conditions):
|
||||||
|
methods = []
|
||||||
|
for condition in conditions:
|
||||||
|
if isinstance(condition, dict) and "methods" in condition:
|
||||||
|
methods.extend(condition["methods"])
|
||||||
|
elif callable(condition) and hasattr(condition, "__name__"):
|
||||||
|
methods.append(condition.__name__)
|
||||||
|
elif isinstance(condition, str):
|
||||||
|
methods.append(condition)
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid condition in and_()")
|
||||||
|
return {"type": "AND", "methods": methods}
|
||||||
|
|
||||||
|
|
||||||
class FlowMeta(type):
|
class FlowMeta(type):
|
||||||
def __new__(mcs, name, bases, dct):
|
def __new__(mcs, name, bases, dct):
|
||||||
cls = super().__new__(mcs, name, bases, dct)
|
cls = super().__new__(mcs, name, bases, dct)
|
||||||
@@ -14,69 +87,51 @@ class FlowMeta(type):
|
|||||||
start_methods = []
|
start_methods = []
|
||||||
listeners = {}
|
listeners = {}
|
||||||
|
|
||||||
|
print(f"[FlowMeta] Processing class: {name}")
|
||||||
for attr_name, attr_value in dct.items():
|
for attr_name, attr_value in dct.items():
|
||||||
|
print(f"[FlowMeta] Checking attribute: {attr_name}")
|
||||||
if hasattr(attr_value, "__is_start_method__"):
|
if hasattr(attr_value, "__is_start_method__"):
|
||||||
|
print(f"[FlowMeta] Found start method: {attr_name}")
|
||||||
start_methods.append(attr_name)
|
start_methods.append(attr_name)
|
||||||
if hasattr(attr_value, "__trigger_methods__"):
|
if hasattr(attr_value, "__trigger_methods__"):
|
||||||
condition = attr_value.__trigger_methods__
|
methods = attr_value.__trigger_methods__
|
||||||
if callable(condition):
|
condition_type = getattr(attr_value, "__condition_type__", "OR")
|
||||||
# Single method reference
|
print(f"[FlowMeta] Conditions for {attr_name}:", methods)
|
||||||
method_name = condition.__name__
|
listeners[attr_name] = (condition_type, methods)
|
||||||
if method_name not in listeners:
|
|
||||||
listeners[method_name] = []
|
|
||||||
listeners[method_name].append((attr_name, "SINGLE", [method_name]))
|
|
||||||
elif isinstance(condition, str):
|
|
||||||
# Single method name
|
|
||||||
if condition not in listeners:
|
|
||||||
listeners[condition] = []
|
|
||||||
listeners[condition].append((attr_name, "SINGLE", [condition]))
|
|
||||||
elif isinstance(condition, tuple):
|
|
||||||
# AND or OR condition
|
|
||||||
condition_type = (
|
|
||||||
"AND" if any(item == "and" for item in condition) else "OR"
|
|
||||||
)
|
|
||||||
methods = [
|
|
||||||
m.__name__ if callable(m) else m
|
|
||||||
for m in condition
|
|
||||||
if m != "and" and m != "or"
|
|
||||||
]
|
|
||||||
for method in methods:
|
|
||||||
if method not in listeners:
|
|
||||||
listeners[method] = []
|
|
||||||
listeners[method].append((attr_name, condition_type, methods))
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Invalid listener format for {attr_name}")
|
|
||||||
|
|
||||||
setattr(cls, "_start_methods", start_methods)
|
setattr(cls, "_start_methods", start_methods)
|
||||||
setattr(cls, "_listeners", listeners)
|
setattr(cls, "_listeners", listeners)
|
||||||
|
|
||||||
if "initial_state" in dct:
|
print("[FlowMeta] ALL LISTENERS:", listeners)
|
||||||
initial_state = dct["initial_state"]
|
print("[FlowMeta] START METHODS:", start_methods)
|
||||||
if isinstance(initial_state, type) and issubclass(initial_state, BaseModel):
|
|
||||||
cls.__annotations__["state"] = initial_state
|
|
||||||
elif isinstance(initial_state, dict):
|
|
||||||
cls.__annotations__["state"] = Dict[str, Any]
|
|
||||||
|
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
|
||||||
class Flow(Generic[T], metaclass=FlowMeta):
|
class Flow(Generic[T], metaclass=FlowMeta):
|
||||||
_start_methods: List[str] = []
|
_start_methods: List[str] = []
|
||||||
_listeners: Dict[str, List[tuple[str, str, List[str]]]] = {}
|
_listeners: Dict[str, tuple[str, List[str]]] = {}
|
||||||
initial_state: Union[Type[T], T, None] = None
|
initial_state: Union[Type[T], T, None] = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
print("[Flow.__init__] Initializing Flow")
|
||||||
self._methods: Dict[str, Callable] = {}
|
self._methods: Dict[str, Callable] = {}
|
||||||
self._state = self._create_initial_state()
|
self._state = self._create_initial_state()
|
||||||
self._completed_methods: set[str] = set()
|
self._completed_methods: Set[str] = set()
|
||||||
|
self._pending_and_listeners: Dict[str, Set[str]] = {}
|
||||||
|
|
||||||
for method_name in dir(self):
|
for method_name in dir(self):
|
||||||
if callable(getattr(self, method_name)) and not method_name.startswith(
|
if callable(getattr(self, method_name)) and not method_name.startswith(
|
||||||
"__"
|
"__"
|
||||||
):
|
):
|
||||||
|
print(f"[Flow.__init__] Adding method: {method_name}")
|
||||||
self._methods[method_name] = getattr(self, method_name)
|
self._methods[method_name] = getattr(self, method_name)
|
||||||
|
|
||||||
|
print("[Flow.__init__] All methods:", self._methods.keys())
|
||||||
|
print("[Flow.__init__] Listeners:", self._listeners)
|
||||||
|
|
||||||
def _create_initial_state(self) -> T:
|
def _create_initial_state(self) -> T:
|
||||||
|
print("[Flow._create_initial_state] Creating initial state")
|
||||||
if self.initial_state is None:
|
if self.initial_state is None:
|
||||||
return {} # type: ignore
|
return {} # type: ignore
|
||||||
elif isinstance(self.initial_state, type):
|
elif isinstance(self.initial_state, type):
|
||||||
@@ -89,67 +144,81 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
async def kickoff(self):
|
async def kickoff(self):
|
||||||
|
print("[Flow.kickoff] Starting kickoff")
|
||||||
if not self._start_methods:
|
if not self._start_methods:
|
||||||
raise ValueError("No start method defined")
|
raise ValueError("No start method defined")
|
||||||
|
|
||||||
for start_method in self._start_methods:
|
for start_method in self._start_methods:
|
||||||
|
print(f"[Flow.kickoff] Executing start method: {start_method}")
|
||||||
result = await self._execute_method(self._methods[start_method])
|
result = await self._execute_method(self._methods[start_method])
|
||||||
|
print(
|
||||||
|
f"[Flow.kickoff] Start method {start_method} completed. Executing listeners."
|
||||||
|
)
|
||||||
await self._execute_listeners(start_method, result)
|
await self._execute_listeners(start_method, result)
|
||||||
|
|
||||||
async def _execute_method(self, method: Callable, *args, **kwargs):
|
async def _execute_method(self, method: Callable, *args, **kwargs):
|
||||||
|
print(f"[Flow._execute_method] Executing method: {method.__name__}")
|
||||||
if inspect.iscoroutinefunction(method):
|
if inspect.iscoroutinefunction(method):
|
||||||
return await method(*args, **kwargs)
|
return await method(*args, **kwargs)
|
||||||
else:
|
else:
|
||||||
return method(*args, **kwargs)
|
return method(*args, **kwargs)
|
||||||
|
|
||||||
async def _execute_listeners(self, trigger_method: str, result: Any):
|
async def _execute_listeners(self, trigger_method: str, result: Any):
|
||||||
self._completed_methods.add(trigger_method)
|
print(
|
||||||
|
f"[Flow._execute_listeners] Executing listeners for trigger method: {trigger_method}"
|
||||||
if trigger_method in self._listeners:
|
)
|
||||||
listener_tasks = []
|
listener_tasks = []
|
||||||
for listener, condition_type, methods in self._listeners[trigger_method]:
|
for listener, (condition_type, methods) in self._listeners.items():
|
||||||
if condition_type == "OR":
|
print(
|
||||||
if trigger_method in methods:
|
f"[Flow._execute_listeners] Checking listener: {listener}, condition: {condition_type}, methods: {methods}"
|
||||||
listener_tasks.append(
|
)
|
||||||
self._execute_single_listener(listener, result)
|
if condition_type == "OR":
|
||||||
)
|
if trigger_method in methods:
|
||||||
elif condition_type == "AND":
|
print(
|
||||||
if all(method in self._completed_methods for method in methods):
|
f"[Flow._execute_listeners] TRIGGERING METHOD: {listener} due to trigger: {trigger_method}"
|
||||||
listener_tasks.append(
|
)
|
||||||
self._execute_single_listener(listener, result)
|
|
||||||
)
|
|
||||||
elif condition_type == "SINGLE":
|
|
||||||
listener_tasks.append(
|
listener_tasks.append(
|
||||||
self._execute_single_listener(listener, result)
|
self._execute_single_listener(listener, 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]:
|
||||||
|
print(
|
||||||
|
f"[Flow._execute_listeners] All conditions met for listener: {listener}. Executing."
|
||||||
|
)
|
||||||
|
listener_tasks.append(
|
||||||
|
self._execute_single_listener(listener, result)
|
||||||
|
)
|
||||||
|
del self._pending_and_listeners[listener]
|
||||||
|
|
||||||
# Run all listener tasks concurrently and wait for them to complete
|
# Run all listener tasks concurrently and wait for them to complete
|
||||||
await asyncio.gather(*listener_tasks)
|
print(
|
||||||
|
f"[Flow._execute_listeners] Executing {len(listener_tasks)} listener tasks"
|
||||||
|
)
|
||||||
|
await asyncio.gather(*listener_tasks)
|
||||||
|
|
||||||
async def _execute_single_listener(self, listener: str, result: Any):
|
async def _execute_single_listener(self, listener: str, result: Any):
|
||||||
|
print(f"[Flow._execute_single_listener] Executing listener: {listener}")
|
||||||
try:
|
try:
|
||||||
method = self._methods[listener]
|
method = self._methods[listener]
|
||||||
sig = inspect.signature(method)
|
sig = inspect.signature(method)
|
||||||
if len(sig.parameters) > 1: # More than just 'self'
|
if len(sig.parameters) > 1: # More than just 'self'
|
||||||
|
print(
|
||||||
|
f"[Flow._execute_single_listener] Executing {listener} with result"
|
||||||
|
)
|
||||||
listener_result = await self._execute_method(method, result)
|
listener_result = await self._execute_method(method, result)
|
||||||
else:
|
else:
|
||||||
|
print(
|
||||||
|
f"[Flow._execute_single_listener] Executing {listener} without result"
|
||||||
|
)
|
||||||
listener_result = await self._execute_method(method)
|
listener_result = await self._execute_method(method)
|
||||||
|
print(
|
||||||
|
f"[Flow._execute_single_listener] {listener} completed, executing its listeners"
|
||||||
|
)
|
||||||
await self._execute_listeners(listener, listener_result)
|
await self._execute_listeners(listener, listener_result)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error in method {listener}: {str(e)}")
|
print(
|
||||||
|
f"[Flow._execute_single_listener] Error in method {listener}: {str(e)}"
|
||||||
|
)
|
||||||
def start():
|
|
||||||
def decorator(func):
|
|
||||||
func.__is_start_method__ = True
|
|
||||||
return func
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def listen(condition):
|
|
||||||
def decorator(func):
|
|
||||||
func.__trigger_methods__ = condition
|
|
||||||
return func
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class ExampleState(BaseModel):
|
|||||||
message: str = ""
|
message: str = ""
|
||||||
|
|
||||||
|
|
||||||
class StructuredExampleFlow(Flow[ExampleState]):
|
class StructuredExampleFlow(Flow):
|
||||||
initial_state = ExampleState
|
initial_state = ExampleState
|
||||||
|
|
||||||
@start()
|
@start()
|
||||||
@@ -41,7 +41,7 @@ class StructuredExampleFlow(Flow[ExampleState]):
|
|||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
flow = StructuredExampleFlow()
|
flow = StructuredExampleFlow()
|
||||||
await flow.run()
|
await flow.kickoff()
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class ExampleState(BaseModel):
|
|||||||
message: str = ""
|
message: str = ""
|
||||||
|
|
||||||
|
|
||||||
class StructuredExampleFlow(Flow[ExampleState]):
|
class StructuredExampleFlow(Flow):
|
||||||
initial_state = ExampleState
|
initial_state = ExampleState
|
||||||
|
|
||||||
@start()
|
@start()
|
||||||
@@ -21,27 +21,22 @@ class StructuredExampleFlow(Flow[ExampleState]):
|
|||||||
return "Start result"
|
return "Start result"
|
||||||
|
|
||||||
@listen(start_method)
|
@listen(start_method)
|
||||||
async def second_method(self, result):
|
async def second_method(self):
|
||||||
print(f"Second method, received: {result}")
|
|
||||||
print(f"State before increment: {self.state}")
|
print(f"State before increment: {self.state}")
|
||||||
self.state.counter += 1
|
self.state.counter += 1
|
||||||
self.state.message += " - updated"
|
self.state.message += " - updated"
|
||||||
print(f"State after second_method: {self.state}")
|
print(f"State after second_method: {self.state}")
|
||||||
return "Second result"
|
return "Second result"
|
||||||
|
|
||||||
@listen(start_method)
|
@listen(start_method and second_method)
|
||||||
async def third_method(self, result):
|
async def logger(self):
|
||||||
print(f"Third method, received: {result}")
|
print("AND METHOD RUNNING")
|
||||||
print(f"State before increment: {self.state}")
|
print("CURRENT STATE FROM OR: ", self.state)
|
||||||
self.state.counter += 1
|
|
||||||
self.state.message += " - updated"
|
|
||||||
print(f"State after third_method: {self.state}")
|
|
||||||
return "Third result"
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
flow = StructuredExampleFlow()
|
flow = StructuredExampleFlow()
|
||||||
await flow.run()
|
await flow.kickoff()
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from crewai.flow.flow import Flow, listen, start
|
from crewai.flow.flow import Flow, and_, listen, or_, start
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
@@ -21,23 +21,27 @@ class StructuredExampleFlow(Flow[ExampleState]):
|
|||||||
return "Start result"
|
return "Start result"
|
||||||
|
|
||||||
@listen(start_method)
|
@listen(start_method)
|
||||||
async def second_method(self, result):
|
async def second_method(self):
|
||||||
print(f"Second method, received: {result}")
|
|
||||||
print(f"State before increment: {self.state}")
|
print(f"State before increment: {self.state}")
|
||||||
self.state.counter += 1
|
self.state.counter += 1
|
||||||
self.state.message += " - updated"
|
self.state.message += " - updated"
|
||||||
print(f"State after second_method: {self.state}")
|
print(f"State after second_method: {self.state}")
|
||||||
return "Second result"
|
return "Second result"
|
||||||
|
|
||||||
@listen(start_method or second_method)
|
@listen(or_(start_method, second_method))
|
||||||
async def logger(self):
|
async def logger(self):
|
||||||
print("OR METHOD RUNNING")
|
print("LOGGER METHOD RUNNING")
|
||||||
print("CURRENT STATE FROM OR: ", self.state)
|
print("CURRENT STATE FROM LOGGER: ", self.state)
|
||||||
|
|
||||||
|
@listen(and_(start_method, second_method))
|
||||||
|
async def and_logger(self):
|
||||||
|
print("AND LOGGER METHOD RUNNING")
|
||||||
|
print("CURRENT STATE FROM AND LOGGER: ", self.state)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
flow = StructuredExampleFlow()
|
flow = StructuredExampleFlow()
|
||||||
await flow.run()
|
await flow.kickoff()
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
from crewai.flow.flow import Flow, listen, start
|
from crewai.flow.flow import Flow, listen, start
|
||||||
|
|
||||||
|
|
||||||
@@ -23,6 +25,9 @@ class FlexibleExampleFlow(Flow):
|
|||||||
return "Third result"
|
return "Third result"
|
||||||
|
|
||||||
|
|
||||||
# Run the flows
|
async def main():
|
||||||
flexible_flow = FlexibleExampleFlow()
|
flow = FlexibleExampleFlow()
|
||||||
flexible_flow.run()
|
await flow.kickoff()
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
|||||||
Reference in New Issue
Block a user