soe-ai 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- soe/builtin_tools/__init__.py +39 -0
- soe/builtin_tools/soe_add_signal.py +82 -0
- soe/builtin_tools/soe_call_tool.py +111 -0
- soe/builtin_tools/soe_copy_context.py +80 -0
- soe/builtin_tools/soe_explore_docs.py +290 -0
- soe/builtin_tools/soe_get_available_tools.py +42 -0
- soe/builtin_tools/soe_get_context.py +50 -0
- soe/builtin_tools/soe_get_workflows.py +63 -0
- soe/builtin_tools/soe_inject_node.py +86 -0
- soe/builtin_tools/soe_inject_workflow.py +105 -0
- soe/builtin_tools/soe_list_contexts.py +73 -0
- soe/builtin_tools/soe_remove_node.py +72 -0
- soe/builtin_tools/soe_remove_workflow.py +62 -0
- soe/builtin_tools/soe_update_context.py +54 -0
- soe/docs/_config.yml +10 -0
- soe/docs/advanced_patterns/guide_fanout_and_aggregations.md +318 -0
- soe/docs/advanced_patterns/guide_inheritance.md +435 -0
- soe/docs/advanced_patterns/hybrid_intelligence.md +237 -0
- soe/docs/advanced_patterns/index.md +49 -0
- soe/docs/advanced_patterns/operational.md +781 -0
- soe/docs/advanced_patterns/self_evolving_workflows.md +385 -0
- soe/docs/advanced_patterns/swarm_intelligence.md +211 -0
- soe/docs/builtins/context.md +164 -0
- soe/docs/builtins/explore_docs.md +135 -0
- soe/docs/builtins/tools.md +164 -0
- soe/docs/builtins/workflows.md +199 -0
- soe/docs/guide_00_getting_started.md +341 -0
- soe/docs/guide_01_tool.md +206 -0
- soe/docs/guide_02_llm.md +143 -0
- soe/docs/guide_03_router.md +146 -0
- soe/docs/guide_04_patterns.md +475 -0
- soe/docs/guide_05_agent.md +159 -0
- soe/docs/guide_06_schema.md +397 -0
- soe/docs/guide_07_identity.md +540 -0
- soe/docs/guide_08_child.md +612 -0
- soe/docs/guide_09_ecosystem.md +690 -0
- soe/docs/guide_10_infrastructure.md +427 -0
- soe/docs/guide_11_builtins.md +118 -0
- soe/docs/index.md +104 -0
- soe/docs/primitives/backends.md +281 -0
- soe/docs/primitives/context.md +256 -0
- soe/docs/primitives/node_reference.md +259 -0
- soe/docs/primitives/primitives.md +331 -0
- soe/docs/primitives/signals.md +865 -0
- soe/docs_index.py +1 -1
- soe/lib/__init__.py +0 -0
- soe/lib/child_context.py +46 -0
- soe/lib/context_fields.py +51 -0
- soe/lib/inheritance.py +172 -0
- soe/lib/jinja_render.py +113 -0
- soe/lib/operational.py +51 -0
- soe/lib/parent_sync.py +71 -0
- soe/lib/register_event.py +75 -0
- soe/lib/schema_validation.py +134 -0
- soe/lib/yaml_parser.py +14 -0
- soe/local_backends/__init__.py +18 -0
- soe/local_backends/factory.py +124 -0
- soe/local_backends/in_memory/context.py +38 -0
- soe/local_backends/in_memory/conversation_history.py +60 -0
- soe/local_backends/in_memory/identity.py +52 -0
- soe/local_backends/in_memory/schema.py +40 -0
- soe/local_backends/in_memory/telemetry.py +38 -0
- soe/local_backends/in_memory/workflow.py +33 -0
- soe/local_backends/storage/context.py +57 -0
- soe/local_backends/storage/conversation_history.py +82 -0
- soe/local_backends/storage/identity.py +118 -0
- soe/local_backends/storage/schema.py +96 -0
- soe/local_backends/storage/telemetry.py +72 -0
- soe/local_backends/storage/workflow.py +56 -0
- soe/nodes/__init__.py +13 -0
- soe/nodes/agent/__init__.py +10 -0
- soe/nodes/agent/factory.py +134 -0
- soe/nodes/agent/lib/loop_handlers.py +150 -0
- soe/nodes/agent/lib/loop_state.py +157 -0
- soe/nodes/agent/lib/prompts.py +65 -0
- soe/nodes/agent/lib/tools.py +35 -0
- soe/nodes/agent/stages/__init__.py +12 -0
- soe/nodes/agent/stages/parameter.py +37 -0
- soe/nodes/agent/stages/response.py +54 -0
- soe/nodes/agent/stages/router.py +37 -0
- soe/nodes/agent/state.py +111 -0
- soe/nodes/agent/types.py +66 -0
- soe/nodes/agent/validation/__init__.py +11 -0
- soe/nodes/agent/validation/config.py +95 -0
- soe/nodes/agent/validation/operational.py +24 -0
- soe/nodes/child/__init__.py +3 -0
- soe/nodes/child/factory.py +61 -0
- soe/nodes/child/state.py +59 -0
- soe/nodes/child/validation/__init__.py +11 -0
- soe/nodes/child/validation/config.py +126 -0
- soe/nodes/child/validation/operational.py +28 -0
- soe/nodes/lib/conditions.py +71 -0
- soe/nodes/lib/context.py +24 -0
- soe/nodes/lib/conversation_history.py +77 -0
- soe/nodes/lib/identity.py +64 -0
- soe/nodes/lib/llm_resolver.py +142 -0
- soe/nodes/lib/output.py +68 -0
- soe/nodes/lib/response_builder.py +91 -0
- soe/nodes/lib/signal_emission.py +79 -0
- soe/nodes/lib/signals.py +54 -0
- soe/nodes/lib/tools.py +100 -0
- soe/nodes/llm/__init__.py +7 -0
- soe/nodes/llm/factory.py +103 -0
- soe/nodes/llm/state.py +76 -0
- soe/nodes/llm/types.py +12 -0
- soe/nodes/llm/validation/__init__.py +11 -0
- soe/nodes/llm/validation/config.py +89 -0
- soe/nodes/llm/validation/operational.py +23 -0
- soe/nodes/router/__init__.py +3 -0
- soe/nodes/router/factory.py +37 -0
- soe/nodes/router/state.py +32 -0
- soe/nodes/router/validation/__init__.py +11 -0
- soe/nodes/router/validation/config.py +58 -0
- soe/nodes/router/validation/operational.py +16 -0
- soe/nodes/tool/factory.py +66 -0
- soe/nodes/tool/lib/__init__.py +11 -0
- soe/nodes/tool/lib/conditions.py +35 -0
- soe/nodes/tool/lib/failure.py +28 -0
- soe/nodes/tool/lib/parameters.py +67 -0
- soe/nodes/tool/state.py +66 -0
- soe/nodes/tool/types.py +27 -0
- soe/nodes/tool/validation/__init__.py +15 -0
- soe/nodes/tool/validation/config.py +132 -0
- soe/nodes/tool/validation/operational.py +16 -0
- soe/validation/__init__.py +18 -0
- soe/validation/config.py +195 -0
- soe/validation/jinja.py +54 -0
- soe/validation/operational.py +110 -0
- {soe_ai-0.1.1.dist-info → soe_ai-0.1.3.dist-info}/METADATA +5 -5
- soe_ai-0.1.3.dist-info/RECORD +137 -0
- {soe_ai-0.1.1.dist-info → soe_ai-0.1.3.dist-info}/WHEEL +1 -1
- soe_ai-0.1.1.dist-info/RECORD +0 -10
- {soe_ai-0.1.1.dist-info → soe_ai-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {soe_ai-0.1.1.dist-info → soe_ai-0.1.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
|
|
2
|
+
# Advanced Pattern: Fan-Out, Fan-In & Aggregations
|
|
3
|
+
|
|
4
|
+
This guide covers advanced patterns for parallel processing and data aggregation using SOE's context history features.
|
|
5
|
+
|
|
6
|
+
## The Hidden Power of Context: History Lists
|
|
7
|
+
|
|
8
|
+
Until now, we've treated context fields as single values. However, SOE actually stores every update to a field in a **history list**.
|
|
9
|
+
|
|
10
|
+
- **Standard Behavior:** `{{ context.field }}` returns the *last* value (most recent update).
|
|
11
|
+
- **Advanced Behavior:** You can access the *full history* of updates to perform aggregations, fan-out operations, and complex logic.
|
|
12
|
+
|
|
13
|
+
This architecture enables powerful distributed patterns without complex infrastructure code.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Pattern 1: Fan-Out (Parallel Processing)
|
|
18
|
+
|
|
19
|
+
Fan-Out allows you to spawn a child workflow for *each item* in a context field's history. This is true distributable parallelism.
|
|
20
|
+
|
|
21
|
+
### The `fan_out_field` Parameter
|
|
22
|
+
|
|
23
|
+
The `child` node type has a special parameter `fan_out_field`.
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
SpawnWorkers:
|
|
27
|
+
node_type: child
|
|
28
|
+
event_triggers: [DATA_READY]
|
|
29
|
+
child_workflow_name: worker_workflow
|
|
30
|
+
|
|
31
|
+
# The Magic: Spawn one child for each item in this field's history
|
|
32
|
+
fan_out_field: items_to_process
|
|
33
|
+
|
|
34
|
+
# Each child receives one item in this field
|
|
35
|
+
child_input_field: current_item
|
|
36
|
+
|
|
37
|
+
# Optional: Sleep between spawns to prevent rate limits
|
|
38
|
+
spawn_interval: 0.1
|
|
39
|
+
|
|
40
|
+
# Standard child communication
|
|
41
|
+
signals_to_parent: [WORKER_DONE]
|
|
42
|
+
context_updates_to_parent: [worker_results]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**How it works:**
|
|
46
|
+
1. If `items_to_process` has 5 items in its history.
|
|
47
|
+
2. The node spawns 5 independent child workflows.
|
|
48
|
+
3. Each child gets one item injected into `current_item`.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Pattern 2: Fan-In (Waiting for Completion)
|
|
53
|
+
|
|
54
|
+
To synchronize parallel workers, the parent workflow must wait until all children have completed.
|
|
55
|
+
|
|
56
|
+
### The Router Check
|
|
57
|
+
|
|
58
|
+
Use a `router` to compare the number of results with the number of input items.
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
CheckCompletion:
|
|
62
|
+
node_type: router
|
|
63
|
+
event_triggers: [WORKER_DONE]
|
|
64
|
+
event_emissions:
|
|
65
|
+
# Compare the length of the results history vs input history
|
|
66
|
+
- condition: "{{ context.worker_results | accumulated | length == context.items_to_process | accumulated | length }}"
|
|
67
|
+
signal_name: ALL_DONE
|
|
68
|
+
|
|
69
|
+
- condition: "{{ context.worker_results | accumulated | length < context.items_to_process | accumulated | length }}"
|
|
70
|
+
signal_name: WAITING
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Note:** The `| accumulated` filter returns the full history list of a field. The `| length` filter then returns the count of items in that list.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Pattern 3: Aggregation (Processing History)
|
|
78
|
+
|
|
79
|
+
Once data is collected, you often need to aggregate it (summarize, vote, select).
|
|
80
|
+
|
|
81
|
+
### Tool Aggregation
|
|
82
|
+
|
|
83
|
+
You can configure a tool to receive the *full history list* instead of just the last value.
|
|
84
|
+
|
|
85
|
+
**Tool Registry:**
|
|
86
|
+
```python
|
|
87
|
+
tools_registry = {
|
|
88
|
+
"summarize_results": {
|
|
89
|
+
"function": my_summary_function,
|
|
90
|
+
"process_accumulated": True # <--- This enables history access
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
When `process_accumulated: True`, the tool function receives the entire history list as its first positional argument.
|
|
96
|
+
|
|
97
|
+
### LLM Aggregation (The "Judge" Pattern)
|
|
98
|
+
|
|
99
|
+
You can also pass the full history to an LLM prompt using the `| accumulated` filter.
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
JudgeBestResult:
|
|
103
|
+
node_type: llm
|
|
104
|
+
event_triggers: [ALL_DONE]
|
|
105
|
+
prompt: |
|
|
106
|
+
Here are the proposed solutions:
|
|
107
|
+
{{ context.worker_results | accumulated }}
|
|
108
|
+
|
|
109
|
+
Please select the best solution and explain why.
|
|
110
|
+
output_field: best_solution
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Complete Workflow Examples
|
|
116
|
+
|
|
117
|
+
### Example 1: Fan-Out / Fan-In with Tool Aggregation
|
|
118
|
+
|
|
119
|
+
This complete workflow demonstrates the full pattern: spawn workers, wait for completion, aggregate results.
|
|
120
|
+
|
|
121
|
+
```yaml
|
|
122
|
+
main_workflow:
|
|
123
|
+
SpawnWorkers:
|
|
124
|
+
node_type: child
|
|
125
|
+
event_triggers: [START]
|
|
126
|
+
child_workflow_name: worker_workflow
|
|
127
|
+
child_initial_signals: [START]
|
|
128
|
+
fan_out_field: items_to_process
|
|
129
|
+
child_input_field: current_item
|
|
130
|
+
signals_to_parent: [WORKER_DONE]
|
|
131
|
+
context_updates_to_parent: [worker_result]
|
|
132
|
+
|
|
133
|
+
CheckCompletion:
|
|
134
|
+
node_type: router
|
|
135
|
+
event_triggers: [WORKER_DONE]
|
|
136
|
+
event_emissions:
|
|
137
|
+
- condition: "{{ context.worker_result | accumulated | length == context.items_to_process | accumulated | length }}"
|
|
138
|
+
signal_name: ALL_DONE
|
|
139
|
+
- condition: "{{ context.worker_result | accumulated | length < context.items_to_process | accumulated | length }}"
|
|
140
|
+
signal_name: WAITING
|
|
141
|
+
|
|
142
|
+
AggregateResults:
|
|
143
|
+
node_type: tool
|
|
144
|
+
event_triggers: [ALL_DONE]
|
|
145
|
+
tool_name: aggregate
|
|
146
|
+
context_parameter_field: worker_result
|
|
147
|
+
output_field: final_result
|
|
148
|
+
event_emissions:
|
|
149
|
+
- signal_name: COMPLETE
|
|
150
|
+
|
|
151
|
+
worker_workflow:
|
|
152
|
+
ProcessItem:
|
|
153
|
+
node_type: tool
|
|
154
|
+
event_triggers: [START]
|
|
155
|
+
tool_name: process_item
|
|
156
|
+
context_parameter_field: current_item
|
|
157
|
+
output_field: worker_result
|
|
158
|
+
event_emissions:
|
|
159
|
+
- signal_name: WORKER_DONE
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Example 2: The Judge Pattern (LLM Selection)
|
|
163
|
+
|
|
164
|
+
This workflow generates multiple options and uses an LLM to select the best one.
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
main_workflow:
|
|
168
|
+
GenerateTaglines:
|
|
169
|
+
node_type: child
|
|
170
|
+
event_triggers: [START]
|
|
171
|
+
child_workflow_name: creative_workflow
|
|
172
|
+
child_initial_signals: [START]
|
|
173
|
+
fan_out_field: prompts
|
|
174
|
+
child_input_field: creative_prompt
|
|
175
|
+
signals_to_parent: [TAGLINE_READY]
|
|
176
|
+
context_updates_to_parent: [tagline]
|
|
177
|
+
|
|
178
|
+
WaitForTaglines:
|
|
179
|
+
node_type: router
|
|
180
|
+
event_triggers: [TAGLINE_READY]
|
|
181
|
+
event_emissions:
|
|
182
|
+
- condition: "{{ context.tagline | accumulated | length == context.prompts | accumulated | length }}"
|
|
183
|
+
signal_name: ALL_TAGLINES_READY
|
|
184
|
+
|
|
185
|
+
SelectBest:
|
|
186
|
+
node_type: llm
|
|
187
|
+
event_triggers: [ALL_TAGLINES_READY]
|
|
188
|
+
prompt: |
|
|
189
|
+
You are a marketing expert. Review these tagline options:
|
|
190
|
+
{% for t in context.tagline | accumulated %}
|
|
191
|
+
Option {{ loop.index }}: {{ t }}
|
|
192
|
+
{% endfor %}
|
|
193
|
+
|
|
194
|
+
Select the best tagline and explain why in JSON format.
|
|
195
|
+
output_field: winner
|
|
196
|
+
event_emissions:
|
|
197
|
+
- signal_name: COMPLETE
|
|
198
|
+
|
|
199
|
+
creative_workflow:
|
|
200
|
+
GenerateOne:
|
|
201
|
+
node_type: llm
|
|
202
|
+
event_triggers: [START]
|
|
203
|
+
prompt: |
|
|
204
|
+
{{ context.creative_prompt }}
|
|
205
|
+
|
|
206
|
+
Generate a creative tagline. Output as JSON with key "tagline".
|
|
207
|
+
output_field: tagline
|
|
208
|
+
event_emissions:
|
|
209
|
+
- signal_name: TAGLINE_READY
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Example 3: Map-Reduce Pattern
|
|
213
|
+
|
|
214
|
+
This workflow processes data chunks in parallel and reduces to a single summary.
|
|
215
|
+
|
|
216
|
+
```yaml
|
|
217
|
+
main_workflow:
|
|
218
|
+
ProcessChunks:
|
|
219
|
+
node_type: child
|
|
220
|
+
event_triggers: [START]
|
|
221
|
+
child_workflow_name: mapper_workflow
|
|
222
|
+
child_initial_signals: [START]
|
|
223
|
+
fan_out_field: data_chunks
|
|
224
|
+
child_input_field: chunk
|
|
225
|
+
signals_to_parent: [CHUNK_DONE]
|
|
226
|
+
context_updates_to_parent: [chunk_result]
|
|
227
|
+
|
|
228
|
+
WaitForMappers:
|
|
229
|
+
node_type: router
|
|
230
|
+
event_triggers: [CHUNK_DONE]
|
|
231
|
+
event_emissions:
|
|
232
|
+
- condition: "{{ context.chunk_result | accumulated | length == context.data_chunks | accumulated | length }}"
|
|
233
|
+
signal_name: MAP_COMPLETE
|
|
234
|
+
|
|
235
|
+
ReduceResults:
|
|
236
|
+
node_type: tool
|
|
237
|
+
event_triggers: [MAP_COMPLETE]
|
|
238
|
+
tool_name: reduce_results
|
|
239
|
+
context_parameter_field: chunk_result
|
|
240
|
+
output_field: final_summary
|
|
241
|
+
event_emissions:
|
|
242
|
+
- signal_name: COMPLETE
|
|
243
|
+
|
|
244
|
+
mapper_workflow:
|
|
245
|
+
ProcessOneChunk:
|
|
246
|
+
node_type: tool
|
|
247
|
+
event_triggers: [START]
|
|
248
|
+
tool_name: process_chunk
|
|
249
|
+
context_parameter_field: chunk
|
|
250
|
+
output_field: chunk_result
|
|
251
|
+
event_emissions:
|
|
252
|
+
- signal_name: CHUNK_DONE
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Summary of New Parameters
|
|
258
|
+
|
|
259
|
+
### Child Node
|
|
260
|
+
| Parameter | Type | Description |
|
|
261
|
+
|-----------|------|-------------|
|
|
262
|
+
| `fan_out_field` | string | The context field to iterate over. Spawns one child per history item. |
|
|
263
|
+
| `child_input_field` | string | The field name in the child's context where the item will be injected. |
|
|
264
|
+
| `spawn_interval` | float | (Optional) Seconds to sleep between spawns to prevent throttling. |
|
|
265
|
+
|
|
266
|
+
### Tool Registry
|
|
267
|
+
| Key | Type | Description |
|
|
268
|
+
|-----|------|-------------|
|
|
269
|
+
| `process_accumulated` | bool | If True, the tool receives the full history list instead of the last value. |
|
|
270
|
+
|
|
271
|
+
### Jinja Filters
|
|
272
|
+
| Filter | Description |
|
|
273
|
+
|--------|-------------|
|
|
274
|
+
| `| accumulated` | Returns the full history list of the field. |
|
|
275
|
+
| `| accumulated | length` | Returns the number of items in the history list. |
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Non-Dynamic Fan-Out
|
|
280
|
+
|
|
281
|
+
You can create non-dynamic fan-out by explicitly defining child nodes for each worker. This is more verbose but provides explicit control:
|
|
282
|
+
|
|
283
|
+
```yaml
|
|
284
|
+
orchestrator:
|
|
285
|
+
Start:
|
|
286
|
+
node_type: router
|
|
287
|
+
event_triggers: [START]
|
|
288
|
+
event_emissions:
|
|
289
|
+
- signal_name: SPAWN_WORKERS
|
|
290
|
+
|
|
291
|
+
Worker1:
|
|
292
|
+
node_type: child
|
|
293
|
+
event_triggers: [SPAWN_WORKERS]
|
|
294
|
+
child_workflow_name: worker_workflow
|
|
295
|
+
child_initial_signals: [START]
|
|
296
|
+
input_fields: [chunk_1]
|
|
297
|
+
signals_to_parent: [WORKER_DONE]
|
|
298
|
+
context_updates_to_parent: [result_1]
|
|
299
|
+
|
|
300
|
+
Worker2:
|
|
301
|
+
node_type: child
|
|
302
|
+
event_triggers: [SPAWN_WORKERS]
|
|
303
|
+
child_workflow_name: worker_workflow
|
|
304
|
+
child_initial_signals: [START]
|
|
305
|
+
input_fields: [chunk_2]
|
|
306
|
+
signals_to_parent: [WORKER_DONE]
|
|
307
|
+
context_updates_to_parent: [result_2]
|
|
308
|
+
|
|
309
|
+
# ... more workers
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Trade-off**: Explicit control vs. verbose configuration. Use `fan_out_field` for dynamic scenarios.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Related Patterns
|
|
317
|
+
|
|
318
|
+
For more complex multi-agent coordination patterns (voting, consensus, jury systems), see [Swarm Intelligence](swarm_intelligence.md).
|