-
Notifications
You must be signed in to change notification settings - Fork 7.3k
Lorenze/feat/conversational flows #5896
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9e8b47f
eca18b0
f1c5ea3
9f0292c
22f8fa6
f2254d9
d4882c6
e71b916
1bf73b2
38e762d
b8237f6
ff46625
d8c8ab9
4d2909b
1dd3af0
796ac69
5f180de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| """Trace collection listener for orchestrating trace collection.""" | ||
|
|
||
| from datetime import datetime, timezone | ||
| import os | ||
| from typing import Any, ClassVar | ||
| import uuid | ||
|
|
@@ -230,7 +231,15 @@ def on_flow_created(source: Any, event: FlowCreatedEvent) -> None: | |
|
|
||
| @event_bus.on(FlowStartedEvent) | ||
| def on_flow_started(source: Any, event: FlowStartedEvent) -> None: | ||
| # Always call _initialize_flow_batch to claim ownership. | ||
| # Nested flows (e.g. AgentExecutor inside a conversational Flow) must | ||
| # not re-claim an open session batch owned by the parent kickoff. | ||
| if ( | ||
| self.batch_manager.defer_session_finalization | ||
| and self.batch_manager.is_batch_initialized() | ||
| and self.batch_manager.batch_owner_type == "flow" | ||
| ): | ||
| self._handle_trace_event("flow_started", source, event) | ||
| return | ||
| # If batch was already initialized by a concurrent action event | ||
| # (race condition), initialize_batch() returns early but | ||
| # batch_owner_type is still correctly set to "flow". | ||
|
|
@@ -264,18 +273,20 @@ def _register_context_event_handlers(self, event_bus: CrewAIEventsBus) -> None: | |
|
|
||
| @event_bus.on(CrewKickoffStartedEvent) | ||
| def on_crew_started(source: Any, event: CrewKickoffStartedEvent) -> None: | ||
| if self.batch_manager.batch_owner_type != "flow": | ||
| # Always call _initialize_crew_batch to claim ownership. | ||
| # If batch was already initialized by a concurrent action event | ||
| # (e.g. LLM/tool before crew_kickoff_started), initialize_batch() | ||
| # returns early but batch_owner_type is still correctly set to "crew". | ||
| # Skip only when a parent flow already owns the batch. | ||
| # Nested crew inside Flow.kickoff: never claim an existing flow session batch. | ||
| if not self._nested_in_flow_execution() and ( | ||
| not self.batch_manager.is_batch_initialized() | ||
| ): | ||
| self._initialize_crew_batch(source, event) | ||
| self._handle_trace_event("crew_kickoff_started", source, event) | ||
|
|
||
| @event_bus.on(CrewKickoffCompletedEvent) | ||
| def on_crew_completed(source: Any, event: CrewKickoffCompletedEvent) -> None: | ||
| self._handle_trace_event("crew_kickoff_completed", source, event) | ||
| if self.batch_manager.defer_session_finalization: | ||
| return | ||
| if self._nested_in_flow_execution(): | ||
| return | ||
| if self.batch_manager.batch_owner_type == "crew": | ||
| if self.first_time_handler.is_first_time: | ||
| self.first_time_handler.mark_events_collected() | ||
|
|
@@ -286,10 +297,14 @@ def on_crew_completed(source: Any, event: CrewKickoffCompletedEvent) -> None: | |
| @event_bus.on(CrewKickoffFailedEvent) | ||
| def on_crew_failed(source: Any, event: CrewKickoffFailedEvent) -> None: | ||
| self._handle_trace_event("crew_kickoff_failed", source, event) | ||
| if self.batch_manager.defer_session_finalization: | ||
| return | ||
| if self._nested_in_flow_execution(): | ||
| return | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| if self.first_time_handler.is_first_time: | ||
| self.first_time_handler.mark_events_collected() | ||
| self.first_time_handler.handle_execution_completion() | ||
| else: | ||
| elif self.batch_manager.batch_owner_type == "crew": | ||
| self.batch_manager.finalize_batch() | ||
|
|
||
| @event_bus.on(TaskStartedEvent) | ||
|
|
@@ -707,8 +722,32 @@ def _register_system_event_handlers(self, event_bus: CrewAIEventsBus) -> None: | |
| @on_signal | ||
| def handle_signal(source: Any, event: SignalEvent) -> None: | ||
| """Flush trace batch on system signals to prevent data loss.""" | ||
| if self.batch_manager.is_batch_initialized(): | ||
| self.batch_manager.finalize_batch() | ||
| if not self.batch_manager.is_batch_initialized(): | ||
| return | ||
| # Multi-turn flows defer batch finalization to finalize_session_traces(). | ||
| if self.batch_manager.defer_session_finalization: | ||
| return | ||
| self.batch_manager.finalize_batch() | ||
|
|
||
| @staticmethod | ||
| def _is_inside_active_flow_context() -> bool: | ||
| """True when ``kickoff_async`` has set ``current_flow_id`` (nested crew).""" | ||
| from crewai.flow.flow_context import current_flow_id | ||
|
|
||
| return current_flow_id.get() is not None | ||
|
|
||
| def _flow_owns_trace_batch(self) -> bool: | ||
| """True when an in-flight conversational flow already owns the trace batch.""" | ||
| if self.batch_manager.batch_owner_type == "flow": | ||
| return True | ||
| batch = self.batch_manager.current_batch | ||
| if batch is not None: | ||
| return batch.execution_metadata.get("execution_type") == "flow" | ||
| return False | ||
|
|
||
| def _nested_in_flow_execution(self) -> bool: | ||
| """True when a crew runs inside a flow session (context or batch ownership).""" | ||
| return self._is_inside_active_flow_context() or self._flow_owns_trace_batch() | ||
|
|
||
| def _initialize_crew_batch(self, source: Any, event: BaseEvent) -> None: | ||
| """Initialize trace batch. | ||
|
|
@@ -729,6 +768,33 @@ def _initialize_crew_batch(self, source: Any, event: BaseEvent) -> None: | |
|
|
||
| self._initialize_batch(user_context, execution_metadata) | ||
|
|
||
| def _try_initialize_flow_batch_from_context(self, event: Any) -> bool: | ||
| """Claim a flow trace batch when an action event fires inside kickoff. | ||
|
|
||
| When ``suppress_flow_events=True``, console panels are hidden but | ||
| ``FlowStartedEvent`` and method lifecycle events still emit; if no | ||
| batch exists yet, LLM/tool events must not fall back to implicit crew | ||
| batches. | ||
| """ | ||
| from crewai.flow.flow_context import current_flow_id, current_flow_name | ||
|
|
||
| flow_id = current_flow_id.get() | ||
| if flow_id is None: | ||
| return False | ||
|
|
||
| started_at = getattr(event, "timestamp", None) or datetime.now(timezone.utc) | ||
| user_context = self._get_user_context() | ||
| execution_metadata = { | ||
| "flow_name": current_flow_name.get() or "Unknown Flow", | ||
| "execution_start": started_at, | ||
| "crewai_version": get_crewai_version(), | ||
| "execution_type": "flow", | ||
| } | ||
| self.batch_manager.batch_owner_type = "flow" | ||
| self.batch_manager.batch_owner_id = flow_id | ||
| self._initialize_batch(user_context, execution_metadata) | ||
| return True | ||
|
|
||
| def _initialize_flow_batch(self, source: Any, event: BaseEvent) -> None: | ||
| """Initialize trace batch for Flow execution. | ||
|
|
||
|
|
@@ -793,12 +859,19 @@ def _handle_action_event(self, event_type: str, source: Any, event: Any) -> None | |
| event: Event object. | ||
| """ | ||
| if not self.batch_manager.is_batch_initialized(): | ||
| user_context = self._get_user_context() | ||
| execution_metadata = { | ||
| "crew_name": getattr(source, "name", "Unknown Crew"), | ||
| "crewai_version": get_crewai_version(), | ||
| } | ||
| self._initialize_batch(user_context, execution_metadata) | ||
| if self._try_initialize_flow_batch_from_context(event): | ||
| pass | ||
| elif not self._nested_in_flow_execution(): | ||
| user_context = self._get_user_context() | ||
| execution_metadata = { | ||
| "crew_name": getattr(source, "name", "Unknown Crew"), | ||
| "crewai_version": get_crewai_version(), | ||
| } | ||
| self.batch_manager.batch_owner_type = "crew" | ||
| self.batch_manager.batch_owner_id = getattr( | ||
| source, "id", str(uuid.uuid4()) | ||
| ) | ||
| self._initialize_batch(user_context, execution_metadata) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Action events silently dropped when flow batch missingLow Severity In Reviewed by Cursor Bugbot for commit 5f180de. Configure here. |
||
|
|
||
| self.batch_manager.begin_event_processing() | ||
| try: | ||
|
|
||


Uh oh!
There was an error while loading. Please reload this page.