You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/docs/user-guide/conventional-rules.md
+7-6Lines changed: 7 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,18 +14,19 @@ While the platform is designed for maximum flexibility, certain conventions guid
14
14
-**Final Responses**: All output events route to **agent_output_topic**, which the Assistant consumes to return data to the user or caller.
15
15
-**Single Consumer**: Only the Assistant should subscribe to this topic, avoiding conflicting read operations.
16
16
17
-
### Human Request Topic
17
+
### InWorkflow Topics
18
18
19
-
-**Human in the Loop**: Used when user intervention is required; the system posts an `OutputTopicEvent` here, which the Assistant can consume to display prompts or questions.
20
-
-**User Response**: When the user replies, `append_user_input()` posts a `PublishToTopicEvent` (the user’s answer). This message is then read by downstream nodes.
21
-
-**Assistant Role**: The Assistant only consumes `OutputTopicEvent` objects, while nodes consume both the question (`OutputTopicEvent`) and the final user reply (`PublishToTopicEvent`).
19
+
-**Human in the Loop**: Used when user intervention is required within workflows; the system uses paired InWorkflowOutputTopic and InWorkflowInputTopic for coordinated human interactions.
20
+
-**InWorkflowOutputTopic**: Posts `OutputTopicEvent` objects that require human interaction or review.
21
+
-**InWorkflowInputTopic**: Receives user responses via `publish_input_data()` based on upstream events from the paired output topic.
22
+
-**Workflow Coordination**: These topics work together to enable seamless human-in-the-loop workflows with proper event coordination.
22
23
23
24
**Rationale**: Structuring input and output channels ensures clarity, preventing multiple consumers from inadvertently processing final outputs and providing a clear path for user-driven requests.
24
25
25
26
## OutputTopicEvent
26
27
27
-
-**Dedicated for Assistant**: If a newly received event is an `OutputTopicEvent`, the workflow’s `on_event()` skips subscription checks, since only the Assistant should consume it.
28
-
-**Exclusive Destination**: `OutputTopicEvent` can only be published to **agent_output_topic** or **human_request_topic**, ensuring a clear boundary for user-facing outputs.
28
+
-**Dedicated for Assistant**: If a newly received event is an `OutputTopicEvent`, the workflow's `on_event()` skips subscription checks, since only the Assistant should consume it.
29
+
-**Exclusive Destination**: `OutputTopicEvent` can only be published to **agent_output_topic** or **InWorkflowOutputTopic**, ensuring a clear boundary for user-facing outputs.
29
30
30
31
**Rationale**: Limiting `OutputTopicEvent` usage avoids confusion over who should read final results, reinforcing the principle of single responsibility for returning data to the user.
|`_get_consumed_events`| Retrieves and returns consumed events from human request and agent output topics. |
32
+
|`_a_commit_events`| Commits processed events to their respective topics, updating consumer offsets. |
33
+
|`_get_output_events`| Retrieves and returns consumed events from agent output and in-workflow output topics. |
34
34
|`invoke`| Synchronous workflow execution that processes nodes from the invoke queue until completion. |
35
35
|`a_invoke`| Asynchronous workflow execution with streaming support and concurrent node processing. |
36
-
|`_process_all_nodes`| Processes all nodes asynchronously without blocking event streaming, managing concurrent execution. |
37
-
|`_record_consumed_events`| Records consumed output events to the event store with proper streaming handling. |
38
36
|`_invoke_node`| Invokes a single node asynchronously with proper stream handling and error management. |
39
-
|`get_node_input`| Collects and returns input events consumed by a node based on its subscribed topics. |
37
+
|`a_init_workflow`| Asynchronously initializes workflow state, either restoring from stored events or creating new workflow with input data.|
40
38
|`on_event`| Event handler that responds to topic publish events, evaluates node readiness, and queues nodes for execution. |
41
39
|`initial_workflow`| Initializes workflow state, either restoring from stored events or creating new workflow with input data. |
42
40
|`to_dict`| Serializes the workflow to a dictionary representation including nodes, topics, and topic-node mappings. |
@@ -106,10 +104,20 @@ The EventDrivenWorkflow supports both synchronous (`invoke`) and asynchronous (`
106
104
107
105
### Asynchronous Execution (`a_invoke`)
108
106
109
-
1.**Workflow Initialization**: Sets up initial state
110
-
2.**Concurrent Processing**: Uses `_process_all_nodes` to handle nodes concurrently
111
-
3.**Event Streaming**: Streams events as they arrive from `agent_output_topic.event_queue`
112
-
4.**Task Management**: Manages running tasks and executing nodes to prevent conflicts
107
+
The asynchronous execution model provides sophisticated event-driven processing with proper coordination:
108
+
109
+
1.**Workflow Initialization**: Sets up initial state with `a_init_workflow`
110
+
2.**Concurrent Node Processing**: Spawns individual tasks for each node using `_invoke_node`
111
+
3.**Output Listening**: Creates listeners for each output topic to capture results
112
+
4.**Event Streaming**: Uses `MergeIdleQueue` to stream events as they become available
113
+
5.**Proper Termination**: Coordinates workflow completion using `AsyncNodeTracker`
114
+
115
+
#### Key Components
116
+
117
+
-**AsyncNodeTracker**: Manages active node state and idle detection
118
+
-**Output Listeners**: Monitor output topics for new events
119
+
-**MergeIdleQueue**: Coordinates between event availability and workflow idle state
120
+
-**Offset Management**: Commits events immediately to prevent duplicates
113
121
114
122
### Event-Driven Node Triggering
115
123
@@ -148,6 +156,129 @@ The system provides flexible subscription expressions through topic expressions
148
156
149
157
-**AND Logic**: Node executes when ALL subscription conditions are met
150
158
-**OR Logic**: Node executes when ANY subscription condition is met
159
+
160
+
## Async Flow Architecture
161
+
162
+
The EventDrivenWorkflow implements a sophisticated async architecture that addresses the challenges of coordinating multiple concurrent nodes while ensuring proper workflow termination and data consistency.
163
+
164
+
### Workflow Components
165
+
166
+
The async workflow relies on two key components for coordination and output management:
167
+
168
+
#### AsyncNodeTracker
169
+
170
+
The `AsyncNodeTracker` manages workflow state and coordination by tracking active nodes, monitoring processing cycles, and detecting idle states. It provides the foundation for proper workflow termination detection.
171
+
172
+
#### AsyncOutputQueue
173
+
174
+
The `AsyncOutputQueue` manages the collection and streaming of output events from multiple topics. It coordinates concurrent listeners and provides a unified async iterator interface for consuming events.
175
+
176
+
For detailed information about these components, see the [Workflow Components documentation](workflow-components.md).
177
+
178
+
### Component Integration
179
+
180
+
The async workflow integrates these components to provide robust event streaming:
asyncfor msgs in node.a_invoke(invoke_context, consumed_events):
234
+
published_events =await a_publish_events(...)
235
+
# Notify downstream nodes immediately
236
+
for event in published_events:
237
+
if event.topic_name inself._topic_nodes:
238
+
topic =self._topics[event.topic_name]
239
+
asyncwith topic.event_cache._cond:
240
+
topic.event_cache._cond.notify_all()
241
+
242
+
# Commit processed events
243
+
awaitself._a_commit_events(...)
244
+
245
+
finally:
246
+
# Signal node is no longer active
247
+
awaitself._tracker.leave(node.name)
248
+
249
+
except asyncio.CancelledError:
250
+
logger.info(f"Node {node.name} was cancelled")
251
+
raise
252
+
```
253
+
254
+
#### Node Coordination Features
255
+
256
+
-**Buffer Management**: Each node maintains its own event buffer
257
+
-**Activity Signaling**: Nodes signal when they become active/inactive
258
+
-**Immediate Notification**: Downstream nodes notified immediately after publishing
259
+
-**Proper Cleanup**: Resources cleaned up on cancellation
260
+
261
+
### Workflow Termination Logic
262
+
263
+
The workflow terminates when all conditions are met:
264
+
265
+
1.**No Active Nodes**: `AsyncNodeTracker` reports idle state
266
+
2.**No Pending Data**: Output topics have no unconsumed data
267
+
3.**No Progress**: Activity count indicates no new processing cycles
268
+
269
+
```python
270
+
# Check for workflow completion
271
+
if tracker.is_idle() andnot topic.can_consume(consumer_name):
272
+
current_activity = tracker.get_activity_count()
273
+
274
+
# If no new activity since last check, we're done
275
+
if current_activity == last_activity_count:
276
+
break
277
+
278
+
last_activity_count = current_activity
279
+
```
280
+
281
+
This multi-layered approach ensures that workflows terminate cleanly without missing data or creating race conditions between upstream completion and downstream activation.
151
282
-**Complex Expressions**: Combination of AND/OR operators for advanced logic
152
283
153
284
**Important Consideration for OR Logic**: When using OR-based subscriptions, nodes are queued as soon as one condition is satisfied. Messages from other subscribed topics in the OR expression may not be available when `get_node_input` executes, potentially causing data inconsistencies. Careful design is recommended when implementing OR-based logic.
0 commit comments