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.
Files changed (134) hide show
  1. soe/builtin_tools/__init__.py +39 -0
  2. soe/builtin_tools/soe_add_signal.py +82 -0
  3. soe/builtin_tools/soe_call_tool.py +111 -0
  4. soe/builtin_tools/soe_copy_context.py +80 -0
  5. soe/builtin_tools/soe_explore_docs.py +290 -0
  6. soe/builtin_tools/soe_get_available_tools.py +42 -0
  7. soe/builtin_tools/soe_get_context.py +50 -0
  8. soe/builtin_tools/soe_get_workflows.py +63 -0
  9. soe/builtin_tools/soe_inject_node.py +86 -0
  10. soe/builtin_tools/soe_inject_workflow.py +105 -0
  11. soe/builtin_tools/soe_list_contexts.py +73 -0
  12. soe/builtin_tools/soe_remove_node.py +72 -0
  13. soe/builtin_tools/soe_remove_workflow.py +62 -0
  14. soe/builtin_tools/soe_update_context.py +54 -0
  15. soe/docs/_config.yml +10 -0
  16. soe/docs/advanced_patterns/guide_fanout_and_aggregations.md +318 -0
  17. soe/docs/advanced_patterns/guide_inheritance.md +435 -0
  18. soe/docs/advanced_patterns/hybrid_intelligence.md +237 -0
  19. soe/docs/advanced_patterns/index.md +49 -0
  20. soe/docs/advanced_patterns/operational.md +781 -0
  21. soe/docs/advanced_patterns/self_evolving_workflows.md +385 -0
  22. soe/docs/advanced_patterns/swarm_intelligence.md +211 -0
  23. soe/docs/builtins/context.md +164 -0
  24. soe/docs/builtins/explore_docs.md +135 -0
  25. soe/docs/builtins/tools.md +164 -0
  26. soe/docs/builtins/workflows.md +199 -0
  27. soe/docs/guide_00_getting_started.md +341 -0
  28. soe/docs/guide_01_tool.md +206 -0
  29. soe/docs/guide_02_llm.md +143 -0
  30. soe/docs/guide_03_router.md +146 -0
  31. soe/docs/guide_04_patterns.md +475 -0
  32. soe/docs/guide_05_agent.md +159 -0
  33. soe/docs/guide_06_schema.md +397 -0
  34. soe/docs/guide_07_identity.md +540 -0
  35. soe/docs/guide_08_child.md +612 -0
  36. soe/docs/guide_09_ecosystem.md +690 -0
  37. soe/docs/guide_10_infrastructure.md +427 -0
  38. soe/docs/guide_11_builtins.md +118 -0
  39. soe/docs/index.md +104 -0
  40. soe/docs/primitives/backends.md +281 -0
  41. soe/docs/primitives/context.md +256 -0
  42. soe/docs/primitives/node_reference.md +259 -0
  43. soe/docs/primitives/primitives.md +331 -0
  44. soe/docs/primitives/signals.md +865 -0
  45. soe/docs_index.py +1 -1
  46. soe/lib/__init__.py +0 -0
  47. soe/lib/child_context.py +46 -0
  48. soe/lib/context_fields.py +51 -0
  49. soe/lib/inheritance.py +172 -0
  50. soe/lib/jinja_render.py +113 -0
  51. soe/lib/operational.py +51 -0
  52. soe/lib/parent_sync.py +71 -0
  53. soe/lib/register_event.py +75 -0
  54. soe/lib/schema_validation.py +134 -0
  55. soe/lib/yaml_parser.py +14 -0
  56. soe/local_backends/__init__.py +18 -0
  57. soe/local_backends/factory.py +124 -0
  58. soe/local_backends/in_memory/context.py +38 -0
  59. soe/local_backends/in_memory/conversation_history.py +60 -0
  60. soe/local_backends/in_memory/identity.py +52 -0
  61. soe/local_backends/in_memory/schema.py +40 -0
  62. soe/local_backends/in_memory/telemetry.py +38 -0
  63. soe/local_backends/in_memory/workflow.py +33 -0
  64. soe/local_backends/storage/context.py +57 -0
  65. soe/local_backends/storage/conversation_history.py +82 -0
  66. soe/local_backends/storage/identity.py +118 -0
  67. soe/local_backends/storage/schema.py +96 -0
  68. soe/local_backends/storage/telemetry.py +72 -0
  69. soe/local_backends/storage/workflow.py +56 -0
  70. soe/nodes/__init__.py +13 -0
  71. soe/nodes/agent/__init__.py +10 -0
  72. soe/nodes/agent/factory.py +134 -0
  73. soe/nodes/agent/lib/loop_handlers.py +150 -0
  74. soe/nodes/agent/lib/loop_state.py +157 -0
  75. soe/nodes/agent/lib/prompts.py +65 -0
  76. soe/nodes/agent/lib/tools.py +35 -0
  77. soe/nodes/agent/stages/__init__.py +12 -0
  78. soe/nodes/agent/stages/parameter.py +37 -0
  79. soe/nodes/agent/stages/response.py +54 -0
  80. soe/nodes/agent/stages/router.py +37 -0
  81. soe/nodes/agent/state.py +111 -0
  82. soe/nodes/agent/types.py +66 -0
  83. soe/nodes/agent/validation/__init__.py +11 -0
  84. soe/nodes/agent/validation/config.py +95 -0
  85. soe/nodes/agent/validation/operational.py +24 -0
  86. soe/nodes/child/__init__.py +3 -0
  87. soe/nodes/child/factory.py +61 -0
  88. soe/nodes/child/state.py +59 -0
  89. soe/nodes/child/validation/__init__.py +11 -0
  90. soe/nodes/child/validation/config.py +126 -0
  91. soe/nodes/child/validation/operational.py +28 -0
  92. soe/nodes/lib/conditions.py +71 -0
  93. soe/nodes/lib/context.py +24 -0
  94. soe/nodes/lib/conversation_history.py +77 -0
  95. soe/nodes/lib/identity.py +64 -0
  96. soe/nodes/lib/llm_resolver.py +142 -0
  97. soe/nodes/lib/output.py +68 -0
  98. soe/nodes/lib/response_builder.py +91 -0
  99. soe/nodes/lib/signal_emission.py +79 -0
  100. soe/nodes/lib/signals.py +54 -0
  101. soe/nodes/lib/tools.py +100 -0
  102. soe/nodes/llm/__init__.py +7 -0
  103. soe/nodes/llm/factory.py +103 -0
  104. soe/nodes/llm/state.py +76 -0
  105. soe/nodes/llm/types.py +12 -0
  106. soe/nodes/llm/validation/__init__.py +11 -0
  107. soe/nodes/llm/validation/config.py +89 -0
  108. soe/nodes/llm/validation/operational.py +23 -0
  109. soe/nodes/router/__init__.py +3 -0
  110. soe/nodes/router/factory.py +37 -0
  111. soe/nodes/router/state.py +32 -0
  112. soe/nodes/router/validation/__init__.py +11 -0
  113. soe/nodes/router/validation/config.py +58 -0
  114. soe/nodes/router/validation/operational.py +16 -0
  115. soe/nodes/tool/factory.py +66 -0
  116. soe/nodes/tool/lib/__init__.py +11 -0
  117. soe/nodes/tool/lib/conditions.py +35 -0
  118. soe/nodes/tool/lib/failure.py +28 -0
  119. soe/nodes/tool/lib/parameters.py +67 -0
  120. soe/nodes/tool/state.py +66 -0
  121. soe/nodes/tool/types.py +27 -0
  122. soe/nodes/tool/validation/__init__.py +15 -0
  123. soe/nodes/tool/validation/config.py +132 -0
  124. soe/nodes/tool/validation/operational.py +16 -0
  125. soe/validation/__init__.py +18 -0
  126. soe/validation/config.py +195 -0
  127. soe/validation/jinja.py +54 -0
  128. soe/validation/operational.py +110 -0
  129. {soe_ai-0.1.1.dist-info → soe_ai-0.1.3.dist-info}/METADATA +5 -5
  130. soe_ai-0.1.3.dist-info/RECORD +137 -0
  131. {soe_ai-0.1.1.dist-info → soe_ai-0.1.3.dist-info}/WHEEL +1 -1
  132. soe_ai-0.1.1.dist-info/RECORD +0 -10
  133. {soe_ai-0.1.1.dist-info → soe_ai-0.1.3.dist-info}/licenses/LICENSE +0 -0
  134. {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: "&#123;&#123; context.worker_result | accumulated | length == context.items_to_process | accumulated | length &#125;&#125;"
138
+ signal_name: ALL_DONE
139
+ - condition: "&#123;&#123; context.worker_result | accumulated | length < context.items_to_process | accumulated | length &#125;&#125;"
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: "&#123;&#123; context.tagline | accumulated | length == context.prompts | accumulated | length &#125;&#125;"
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
+ &#123;% for t in context.tagline | accumulated %&#125;
191
+ Option &#123;&#123; loop.index &#125;&#125;: &#123;&#123; t &#125;&#125;
192
+ &#123;% endfor %&#125;
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
+ &#123;&#123; context.creative_prompt &#125;&#125;
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: "&#123;&#123; context.chunk_result | accumulated | length == context.data_chunks | accumulated | length &#125;&#125;"
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).