mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-27 00:58:13 +00:00
Add MethodExecutionFailedEvent to handle flow method execution failures
- Introduce new MethodExecutionFailedEvent in flow_events module - Update Flow class to catch and emit method execution failures - Add event listener for method execution failure events - Update event-related imports to include new event type - Enhance test coverage for method execution failure handling
This commit is contained in:
@@ -31,6 +31,7 @@ from crewai.utilities.events import (
|
|||||||
MethodExecutionStartedEvent,
|
MethodExecutionStartedEvent,
|
||||||
)
|
)
|
||||||
from crewai.utilities.events.event_bus import event_bus
|
from crewai.utilities.events.event_bus import event_bus
|
||||||
|
from crewai.utilities.events.flow_events import MethodExecutionFailedEvent
|
||||||
from crewai.utilities.printer import Printer
|
from crewai.utilities.printer import Printer
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -808,40 +809,53 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
async def _execute_method(
|
async def _execute_method(
|
||||||
self, method_name: str, method: Callable, *args: Any, **kwargs: Any
|
self, method_name: str, method: Callable, *args: Any, **kwargs: Any
|
||||||
) -> Any:
|
) -> Any:
|
||||||
dumped_params = {f"_{i}": arg for i, arg in enumerate(args)} | (kwargs or {})
|
try:
|
||||||
event_bus.emit(
|
dumped_params = {f"_{i}": arg for i, arg in enumerate(args)} | (kwargs or {})
|
||||||
self,
|
event_bus.emit(
|
||||||
MethodExecutionStartedEvent(
|
self,
|
||||||
type="method_execution_started",
|
MethodExecutionStartedEvent(
|
||||||
method_name=method_name,
|
type="method_execution_started",
|
||||||
flow_name=self.__class__.__name__,
|
method_name=method_name,
|
||||||
params=dumped_params,
|
flow_name=self.__class__.__name__,
|
||||||
state=self._copy_state(),
|
params=dumped_params,
|
||||||
),
|
state=self._copy_state(),
|
||||||
)
|
),
|
||||||
|
)
|
||||||
|
|
||||||
result = (
|
|
||||||
await method(*args, **kwargs)
|
|
||||||
if asyncio.iscoroutinefunction(method)
|
|
||||||
else method(*args, **kwargs)
|
|
||||||
)
|
|
||||||
self._method_outputs.append(result)
|
|
||||||
self._method_execution_counts[method_name] = (
|
|
||||||
self._method_execution_counts.get(method_name, 0) + 1
|
|
||||||
)
|
|
||||||
|
|
||||||
event_bus.emit(
|
result = (
|
||||||
self,
|
await method(*args, **kwargs)
|
||||||
MethodExecutionFinishedEvent(
|
if asyncio.iscoroutinefunction(method)
|
||||||
type="method_execution_finished",
|
else method(*args, **kwargs)
|
||||||
method_name=method_name,
|
)
|
||||||
flow_name=self.__class__.__name__,
|
|
||||||
state=self._copy_state(),
|
|
||||||
result=result,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
return result
|
self._method_outputs.append(result)
|
||||||
|
self._method_execution_counts[method_name] = (
|
||||||
|
self._method_execution_counts.get(method_name, 0) + 1
|
||||||
|
)
|
||||||
|
|
||||||
|
event_bus.emit(
|
||||||
|
self,
|
||||||
|
MethodExecutionFinishedEvent(
|
||||||
|
type="method_execution_finished",
|
||||||
|
method_name=method_name,
|
||||||
|
flow_name=self.__class__.__name__,
|
||||||
|
state=self._copy_state(),
|
||||||
|
result=result,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
event_bus.emit(
|
||||||
|
self,
|
||||||
|
MethodExecutionFailedEvent(
|
||||||
|
type="method_execution_failed",
|
||||||
|
method_name=method_name,
|
||||||
|
flow_name=self.__class__.__name__,
|
||||||
|
error=e,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
async def _execute_listeners(self, trigger_method: str, result: Any) -> None:
|
async def _execute_listeners(self, trigger_method: str, result: Any) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from .flow_events import (
|
|||||||
FlowFinishedEvent,
|
FlowFinishedEvent,
|
||||||
MethodExecutionStartedEvent,
|
MethodExecutionStartedEvent,
|
||||||
MethodExecutionFinishedEvent,
|
MethodExecutionFinishedEvent,
|
||||||
|
MethodExecutionFailedEvent,
|
||||||
)
|
)
|
||||||
from .event_bus import EventBus, event_bus
|
from .event_bus import EventBus, event_bus
|
||||||
from .tool_usage_events import ToolUsageFinishedEvent, ToolUsageErrorEvent
|
from .tool_usage_events import ToolUsageFinishedEvent, ToolUsageErrorEvent
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from .flow_events import (
|
|||||||
FlowCreatedEvent,
|
FlowCreatedEvent,
|
||||||
FlowFinishedEvent,
|
FlowFinishedEvent,
|
||||||
FlowStartedEvent,
|
FlowStartedEvent,
|
||||||
|
MethodExecutionFailedEvent,
|
||||||
MethodExecutionFinishedEvent,
|
MethodExecutionFinishedEvent,
|
||||||
MethodExecutionStartedEvent,
|
MethodExecutionStartedEvent,
|
||||||
)
|
)
|
||||||
@@ -125,9 +126,8 @@ class EventListener(BaseEventListener):
|
|||||||
source._execution_span = self._telemetry.task_started(
|
source._execution_span = self._telemetry.task_started(
|
||||||
crew=source.agent.crew, task=source
|
crew=source.agent.crew, task=source
|
||||||
)
|
)
|
||||||
context = event.context
|
|
||||||
self.logger.log(
|
self.logger.log(
|
||||||
f"📋 Task started: {source.description} Context: {context}",
|
f"📋 Task started: {source.description}",
|
||||||
event.timestamp,
|
event.timestamp,
|
||||||
color=self.color,
|
color=self.color,
|
||||||
)
|
)
|
||||||
@@ -139,7 +139,7 @@ class EventListener(BaseEventListener):
|
|||||||
if source._execution_span:
|
if source._execution_span:
|
||||||
self._telemetry.task_ended(source._execution_span, source, source.agent.crew)
|
self._telemetry.task_ended(source._execution_span, source, source.agent.crew)
|
||||||
self.logger.log(
|
self.logger.log(
|
||||||
f"📋 Task completed: {source.description}",
|
f"✅ Task completed: {source.description}",
|
||||||
event.timestamp,
|
event.timestamp,
|
||||||
color=self.color,
|
color=self.color,
|
||||||
)
|
)
|
||||||
@@ -169,7 +169,7 @@ class EventListener(BaseEventListener):
|
|||||||
@event_bus.on(AgentExecutionCompletedEvent)
|
@event_bus.on(AgentExecutionCompletedEvent)
|
||||||
def on_agent_execution_completed(source, event: AgentExecutionCompletedEvent):
|
def on_agent_execution_completed(source, event: AgentExecutionCompletedEvent):
|
||||||
self.logger.log(
|
self.logger.log(
|
||||||
f"👍 Agent '{event.agent.role}' completed task",
|
f"✅ Agent '{event.agent.role}' completed task",
|
||||||
event.timestamp,
|
event.timestamp,
|
||||||
color=self.color,
|
color=self.color,
|
||||||
)
|
)
|
||||||
@@ -212,6 +212,15 @@ class EventListener(BaseEventListener):
|
|||||||
color=self.color,
|
color=self.color,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@event_bus.on(MethodExecutionFailedEvent)
|
||||||
|
def on_method_execution_failed(source, event: MethodExecutionFailedEvent):
|
||||||
|
self.logger.log(
|
||||||
|
f"❌ Flow Method Failed: '{event.method_name}'",
|
||||||
|
event.timestamp,
|
||||||
|
color=self.color,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@event_bus.on(MethodExecutionFinishedEvent)
|
@event_bus.on(MethodExecutionFinishedEvent)
|
||||||
def on_method_execution_finished(source, event: MethodExecutionFinishedEvent):
|
def on_method_execution_finished(source, event: MethodExecutionFinishedEvent):
|
||||||
self.logger.log(
|
self.logger.log(
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from .flow_events import (
|
|||||||
FlowStartedEvent,
|
FlowStartedEvent,
|
||||||
MethodExecutionFinishedEvent,
|
MethodExecutionFinishedEvent,
|
||||||
MethodExecutionStartedEvent,
|
MethodExecutionStartedEvent,
|
||||||
|
MethodExecutionFailedEvent,
|
||||||
)
|
)
|
||||||
from .task_events import (
|
from .task_events import (
|
||||||
TaskCompletedEvent,
|
TaskCompletedEvent,
|
||||||
@@ -48,6 +49,7 @@ EventTypes = Union[
|
|||||||
FlowFinishedEvent,
|
FlowFinishedEvent,
|
||||||
MethodExecutionStartedEvent,
|
MethodExecutionStartedEvent,
|
||||||
MethodExecutionFinishedEvent,
|
MethodExecutionFinishedEvent,
|
||||||
|
MethodExecutionFailedEvent,
|
||||||
AgentExecutionErrorEvent,
|
AgentExecutionErrorEvent,
|
||||||
ToolUsageFinishedEvent,
|
ToolUsageFinishedEvent,
|
||||||
ToolUsageErrorEvent,
|
ToolUsageErrorEvent,
|
||||||
|
|||||||
@@ -47,6 +47,15 @@ class MethodExecutionFinishedEvent(FlowEvent):
|
|||||||
type: str = "method_execution_finished"
|
type: str = "method_execution_finished"
|
||||||
|
|
||||||
|
|
||||||
|
class MethodExecutionFailedEvent(FlowEvent):
|
||||||
|
"""Event emitted when a flow method fails execution"""
|
||||||
|
|
||||||
|
flow_name: str
|
||||||
|
method_name: str
|
||||||
|
error: Any
|
||||||
|
type: str = "method_execution_failed"
|
||||||
|
|
||||||
|
|
||||||
class FlowFinishedEvent(FlowEvent):
|
class FlowFinishedEvent(FlowEvent):
|
||||||
"""Event emitted when a flow completes execution"""
|
"""Event emitted when a flow completes execution"""
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from crewai.utilities.events.flow_events import (
|
|||||||
FlowCreatedEvent,
|
FlowCreatedEvent,
|
||||||
FlowFinishedEvent,
|
FlowFinishedEvent,
|
||||||
FlowStartedEvent,
|
FlowStartedEvent,
|
||||||
|
MethodExecutionFailedEvent,
|
||||||
MethodExecutionStartedEvent,
|
MethodExecutionStartedEvent,
|
||||||
)
|
)
|
||||||
from crewai.utilities.events.task_events import (
|
from crewai.utilities.events.task_events import (
|
||||||
@@ -468,3 +469,27 @@ def test_flow_emits_created_event():
|
|||||||
assert len(received_events) == 1
|
assert len(received_events) == 1
|
||||||
assert received_events[0].flow_name == "TestFlow"
|
assert received_events[0].flow_name == "TestFlow"
|
||||||
assert received_events[0].type == "flow_created"
|
assert received_events[0].type == "flow_created"
|
||||||
|
|
||||||
|
|
||||||
|
def test_flow_emits_method_execution_failed_event():
|
||||||
|
received_events = []
|
||||||
|
error = Exception("Simulated method failure")
|
||||||
|
|
||||||
|
@event_bus.on(MethodExecutionFailedEvent)
|
||||||
|
def handle_method_failed(source, event):
|
||||||
|
received_events.append(event)
|
||||||
|
|
||||||
|
class TestFlow(Flow[dict]):
|
||||||
|
@start()
|
||||||
|
def begin(self):
|
||||||
|
raise error
|
||||||
|
|
||||||
|
flow = TestFlow()
|
||||||
|
# with pytest.raises(Exception):
|
||||||
|
flow.kickoff()
|
||||||
|
|
||||||
|
assert len(received_events) == 1
|
||||||
|
assert received_events[0].method_name == "begin"
|
||||||
|
assert received_events[0].flow_name == "TestFlow"
|
||||||
|
assert received_events[0].type == "method_execution_failed"
|
||||||
|
assert received_events[0].error == error
|
||||||
|
|||||||
Reference in New Issue
Block a user