soe-ai 0.1.1__py3-none-any.whl → 0.1.2__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.2.dist-info}/METADATA +4 -4
- soe_ai-0.1.2.dist-info/RECORD +137 -0
- {soe_ai-0.1.1.dist-info → soe_ai-0.1.2.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.2.dist-info}/licenses/LICENSE +0 -0
- {soe_ai-0.1.1.dist-info → soe_ai-0.1.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
|
|
2
|
+
# Appendix B: Node Configuration Reference
|
|
3
|
+
|
|
4
|
+
Complete reference for all node configuration parameters across all node types.
|
|
5
|
+
|
|
6
|
+
## Node Types Overview
|
|
7
|
+
|
|
8
|
+
| Node Type | Purpose | LLM Required |
|
|
9
|
+
|-----------|---------|--------------|
|
|
10
|
+
| `router` | Evaluate conditions and emit signals | No |
|
|
11
|
+
| `llm` | Single LLM call with structured output | Yes |
|
|
12
|
+
| `agent` | Multi-turn LLM with tool calling | Yes |
|
|
13
|
+
| `tool` | Execute Python function | No |
|
|
14
|
+
| `child` | Start sub-orchestration | No |
|
|
15
|
+
|
|
16
|
+
## Configuration Parameters
|
|
17
|
+
|
|
18
|
+
### Legend
|
|
19
|
+
|
|
20
|
+
- ✓ = Supported
|
|
21
|
+
- ✗ = Not supported
|
|
22
|
+
- **R** = Required
|
|
23
|
+
- **O** = Optional
|
|
24
|
+
|
|
25
|
+
### Core Parameters
|
|
26
|
+
|
|
27
|
+
| Parameter | Type | Router | LLM | Agent | Tool | Child | Description |
|
|
28
|
+
|-----------|------|--------|-----|-------|------|-------|-------------|
|
|
29
|
+
| `node_type` | `str` | **R** | **R** | **R** | **R** | **R** | Node type identifier |
|
|
30
|
+
| `event_triggers` | `List[str]` | **R** | **R** | **R** | **R** | **R** | Signals that activate this node |
|
|
31
|
+
| `event_emissions` | `List[dict]` | **R** | **O** | **O** | **O** | **O** | Signals to emit (with optional conditions) |
|
|
32
|
+
|
|
33
|
+
### Prompt & Output Parameters
|
|
34
|
+
|
|
35
|
+
| Parameter | Type | Router | LLM | Agent | Tool | Child | Description |
|
|
36
|
+
|-----------|------|--------|-----|-------|------|-------|-------------|
|
|
37
|
+
| `prompt` | `str` | ✗ | **R** | **R** | ✗ | ✗ | Jinja template for LLM prompt |
|
|
38
|
+
| `output_field` | `str` | ✗ | **O** | **O** | **O** | ✗ | Context field to store result |
|
|
39
|
+
| `identity` | `str` | ✗ | **O** | **O** | ✗ | ✗ | Key for conversation history persistence |
|
|
40
|
+
|
|
41
|
+
### Tool Parameters
|
|
42
|
+
|
|
43
|
+
| Parameter | Type | Router | LLM | Agent | Tool | Child | Description |
|
|
44
|
+
|-----------|------|--------|-----|-------|------|-------|-------------|
|
|
45
|
+
| `tool_name` | `str` | ✗ | ✗ | ✗ | **R** | ✗ | Tool to execute from registry |
|
|
46
|
+
| `tools` | `List[str]` | ✗ | ✗ | **O** | ✗ | ✗ | Tool names available to agent |
|
|
47
|
+
| `context_parameter_field` | `str` | ✗ | ✗ | ✗ | **O** | ✗ | Context field containing tool kwargs |
|
|
48
|
+
|
|
49
|
+
### Child Workflow Parameters
|
|
50
|
+
|
|
51
|
+
| Parameter | Type | Router | LLM | Agent | Tool | Child | Description |
|
|
52
|
+
|-----------|------|--------|-----|-------|------|-------|-------------|
|
|
53
|
+
| `child_workflow_name` | `str` | ✗ | ✗ | ✗ | ✗ | **R** | Workflow to start as child |
|
|
54
|
+
| `child_initial_signals` | `List[str]` | ✗ | ✗ | ✗ | ✗ | **R** | Signals to start child with |
|
|
55
|
+
| `signals_to_parent` | `List[str]` | ✗ | ✗ | ✗ | ✗ | **O** | Child signals that propagate to parent |
|
|
56
|
+
| `context_updates_to_parent` | `List[str]` | ✗ | ✗ | ✗ | ✗ | **O** | Context keys that sync to parent |
|
|
57
|
+
| `input_fields` | `List[str]` | ✗ | ✗ | ✗ | ✗ | **O** | Context fields to pass to child |
|
|
58
|
+
| `fan_out_field` | `str` | ✗ | ✗ | ✗ | ✗ | **O** | Spawn one child per item in field's history |
|
|
59
|
+
| `child_input_field` | `str` | ✗ | ✗ | ✗ | ✗ | **O** | Field in child context for each fan-out item |
|
|
60
|
+
| `spawn_interval` | `float` | ✗ | ✗ | ✗ | ✗ | **O** | Seconds to sleep between fan-out spawns |
|
|
61
|
+
|
|
62
|
+
### Retry & Failure Parameters
|
|
63
|
+
|
|
64
|
+
| Parameter | Type | Router | LLM | Agent | Tool | Child | Description |
|
|
65
|
+
|-----------|------|--------|-----|-------|------|-------|-------------|
|
|
66
|
+
| `retries` | `int` | ✗ | **O** | **O** | ✗ | ✗ | Max LLM validation retries (default: 3) |
|
|
67
|
+
| `llm_failure_signal` | `str` | ✗ | **O** | **O** | ✗ | ✗ | Signal when LLM retries exhausted |
|
|
68
|
+
|
|
69
|
+
## Tool Registry Configuration
|
|
70
|
+
|
|
71
|
+
Tools are registered with optional configuration:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
# Simple format
|
|
75
|
+
tools_registry = {
|
|
76
|
+
"send_email": send_email_function,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Extended format
|
|
80
|
+
tools_registry = {
|
|
81
|
+
"send_email": {
|
|
82
|
+
"function": send_email_function,
|
|
83
|
+
"max_retries": 3,
|
|
84
|
+
"failure_signal": "EMAIL_FAILED",
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
| Field | Type | Required | Default | Description |
|
|
90
|
+
|-------|------|----------|---------|-------------|
|
|
91
|
+
| `function` | `Callable` | Yes | - | The Python function to execute |
|
|
92
|
+
| `max_retries` | `int` | No | 1 | Execution retries on failure |
|
|
93
|
+
| `failure_signal` | `str` | No | None | Signal when all retries exhausted |
|
|
94
|
+
| `process_accumulated` | `bool` | No | False | Pass full history list instead of last value |
|
|
95
|
+
|
|
96
|
+
## Event Emissions Format
|
|
97
|
+
|
|
98
|
+
The `event_emissions` parameter accepts a list of signal definitions:
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
event_emissions:
|
|
103
|
+
- signal_name: SUCCESS
|
|
104
|
+
- signal_name: NEEDS_REVIEW
|
|
105
|
+
condition: "{{ result.confidence < 0.8 }}" # Jinja: evaluated programmatically
|
|
106
|
+
- signal_name: POSITIVE
|
|
107
|
+
condition: "User sentiment is positive" # Plain text: for LLM signal selection
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
| Field | Type | Required | Description |
|
|
112
|
+
|-------|------|----------|-----------|
|
|
113
|
+
| `signal_name` | `str` | Yes | Signal to emit |
|
|
114
|
+
| `condition` | `str` | No | Plain text = LLM selects signal; Jinja (`{{ }}`) = evaluated programmatically |
|
|
115
|
+
|
|
116
|
+
## Parameter Details
|
|
117
|
+
|
|
118
|
+
### `event_triggers`
|
|
119
|
+
|
|
120
|
+
Signals that activate the node. Uses OR logic by default.
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
event_triggers: [START, RETRY, MANUAL_TRIGGER]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `event_emissions`
|
|
127
|
+
|
|
128
|
+
Signals to emit after node execution. Behavior varies by node type:
|
|
129
|
+
|
|
130
|
+
| Node Type | Condition Context | Default Behavior |
|
|
131
|
+
|-----------|-------------------|------------------|
|
|
132
|
+
| Router | `context` | Emits first matching condition |
|
|
133
|
+
| LLM | N/A (uses `description` for LLM selection) | Emits all if no descriptions |
|
|
134
|
+
| Agent | N/A (uses `description` for LLM selection) | Emits all if no descriptions |
|
|
135
|
+
| Tool | `result` and `context` | Emits all on success |
|
|
136
|
+
| Child | `context` | Emits all after child starts |
|
|
137
|
+
|
|
138
|
+
### `prompt`
|
|
139
|
+
|
|
140
|
+
Jinja template with access to `context`:
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
prompt: "Analyze the following: {{ context.user_input }}"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
### `identity`
|
|
149
|
+
|
|
150
|
+
Enables conversation history persistence. Same identity = shared history.
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
```yaml
|
|
154
|
+
identity: "user_session_123"
|
|
155
|
+
# Or dynamic:
|
|
156
|
+
identity: "{{ context.session_id }}"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
### `retries`
|
|
161
|
+
|
|
162
|
+
Controls LLM validation retry attempts when response doesn't match expected schema.
|
|
163
|
+
|
|
164
|
+
```yaml
|
|
165
|
+
retries: 5 # Default is 3
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `llm_failure_signal`
|
|
169
|
+
|
|
170
|
+
Emit signal instead of raising exception when LLM retries are exhausted:
|
|
171
|
+
|
|
172
|
+
```yaml
|
|
173
|
+
llm_failure_signal: LLM_FAILED
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Quick Reference by Node Type
|
|
177
|
+
|
|
178
|
+
### Router Node
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
```yaml
|
|
182
|
+
MyRouter:
|
|
183
|
+
node_type: router
|
|
184
|
+
event_triggers: [START] # Required
|
|
185
|
+
event_emissions: # Required
|
|
186
|
+
- signal_name: VALID
|
|
187
|
+
condition: "{{ context.input }}"
|
|
188
|
+
- signal_name: INVALID
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
### LLM Node
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
```yaml
|
|
196
|
+
MyLLM:
|
|
197
|
+
node_type: llm
|
|
198
|
+
event_triggers: [START] # Required
|
|
199
|
+
prompt: "Process: {{ context.data }}" # Required
|
|
200
|
+
output_field: result # Optional
|
|
201
|
+
identity: session_123 # Optional
|
|
202
|
+
retries: 3 # Optional (default: 3)
|
|
203
|
+
llm_failure_signal: LLM_FAILED # Optional
|
|
204
|
+
event_emissions: # Optional
|
|
205
|
+
- signal_name: DONE
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
### Agent Node
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
```yaml
|
|
213
|
+
MyAgent:
|
|
214
|
+
node_type: agent
|
|
215
|
+
event_triggers: [START] # Required
|
|
216
|
+
prompt: "Help with: {{ context.task }}" # Required
|
|
217
|
+
tools: [search, calculate] # Optional
|
|
218
|
+
output_field: result # Optional
|
|
219
|
+
identity: agent_session # Optional
|
|
220
|
+
retries: 3 # Optional (default: 3)
|
|
221
|
+
llm_failure_signal: AGENT_FAILED # Optional
|
|
222
|
+
event_emissions: # Optional
|
|
223
|
+
- signal_name: DONE
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
### Tool Node
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
```yaml
|
|
231
|
+
MyTool:
|
|
232
|
+
node_type: tool
|
|
233
|
+
event_triggers: [START] # Required
|
|
234
|
+
tool_name: send_email # Required
|
|
235
|
+
context_parameter_field: email_data # Optional
|
|
236
|
+
output_field: email_result # Optional
|
|
237
|
+
event_emissions: # Optional
|
|
238
|
+
- signal_name: SENT
|
|
239
|
+
- signal_name: NEEDS_RETRY
|
|
240
|
+
condition: "{{ result.status == 'pending' }}"
|
|
241
|
+
- signal_name: HIGH_PRIORITY
|
|
242
|
+
condition: "{{ result.sent and context.email_data.priority == 'high' }}"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
### Child Node
|
|
247
|
+
|
|
248
|
+
```yaml
|
|
249
|
+
MyChild:
|
|
250
|
+
node_type: child
|
|
251
|
+
event_triggers: [START] # Required
|
|
252
|
+
child_workflow_name: sub_workflow # Required
|
|
253
|
+
child_initial_signals: [BEGIN] # Required
|
|
254
|
+
input_fields: [user_data] # Optional
|
|
255
|
+
signals_to_parent: [CHILD_DONE] # Optional
|
|
256
|
+
context_updates_to_parent: [result] # Optional
|
|
257
|
+
event_emissions: # Optional
|
|
258
|
+
- signal_name: CHILD_STARTED
|
|
259
|
+
```
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# SOE Concepts: The 7 Primitives
|
|
2
|
+
|
|
3
|
+
SOE is built on **7 core primitives**. Understanding these gives you complete mastery of the system.
|
|
4
|
+
|
|
5
|
+
| Primitive | Purpose |
|
|
6
|
+
|-----------|---------|
|
|
7
|
+
| **Signals** | Communication between nodes |
|
|
8
|
+
| **Context** | Shared state dictionary |
|
|
9
|
+
| **Workflows** | YAML definitions of nodes |
|
|
10
|
+
| **Backends** | Pluggable storage |
|
|
11
|
+
| **Nodes** | Execution units |
|
|
12
|
+
| **Identities** | System prompts for LLMs |
|
|
13
|
+
| **Context Schema** | Type validation for outputs |
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 1. Signals
|
|
18
|
+
|
|
19
|
+
**What:** Named events that flow through the system.
|
|
20
|
+
|
|
21
|
+
**Purpose:** Communication between nodes. Signals are the *only* way nodes interact.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
```yaml
|
|
25
|
+
event_emissions:
|
|
26
|
+
- signal_name: USER_VALIDATED
|
|
27
|
+
condition: "{{ context.user_id is defined }}"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
**Key Properties:**
|
|
32
|
+
- Signals are strings (e.g., `START`, `COMPLETE`, `ERROR`)
|
|
33
|
+
- Multiple signals can be emitted at once
|
|
34
|
+
- Signals trigger nodes via `event_triggers`
|
|
35
|
+
- Signals are stateless—they carry no payload (use Context for data)
|
|
36
|
+
|
|
37
|
+
**Examples:**
|
|
38
|
+
- `START` — Initial trigger for a workflow
|
|
39
|
+
- `TOOL_SUCCESS` — A tool completed successfully
|
|
40
|
+
- `AGENT_NEEDS_INPUT` — Agent requires user interaction
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 2. Context
|
|
45
|
+
|
|
46
|
+
**What:** A shared dictionary of data for the current execution.
|
|
47
|
+
|
|
48
|
+
**Purpose:** State management. All nodes read from and write to Context.
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
context = backends.context.get_context(execution_id)
|
|
52
|
+
# {"user_id": "123", "validated": True, "result": "Hello!"}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Key Properties:**
|
|
56
|
+
- One Context per execution ID
|
|
57
|
+
- Persisted via `ContextBackend`
|
|
58
|
+
- LLM/Agent/Tool nodes can update Context via `output_field`
|
|
59
|
+
- Jinja2 templates access Context: `{{ context.user_id }}`
|
|
60
|
+
|
|
61
|
+
**Examples:**
|
|
62
|
+
```yaml
|
|
63
|
+
# LLM/Agent nodes store results via output_field
|
|
64
|
+
output_field: result
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 3. Workflows
|
|
70
|
+
|
|
71
|
+
**What:** YAML definitions that describe node configurations and their relationships.
|
|
72
|
+
|
|
73
|
+
**Purpose:** The blueprint for orchestration. Defines *what* happens, not *how*.
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
my_workflow:
|
|
78
|
+
ValidateInput:
|
|
79
|
+
node_type: router
|
|
80
|
+
event_triggers: [START]
|
|
81
|
+
event_emissions:
|
|
82
|
+
- signal_name: VALID
|
|
83
|
+
condition: "{{ context.data is defined }}"
|
|
84
|
+
|
|
85
|
+
ProcessData:
|
|
86
|
+
node_type: tool
|
|
87
|
+
event_triggers: [VALID]
|
|
88
|
+
tool_name: process
|
|
89
|
+
input_fields: [data]
|
|
90
|
+
output_field: result
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
**Key Properties:**
|
|
95
|
+
- Multiple workflows can exist in one registry
|
|
96
|
+
- Workflows are parsed at runtime (Jinja2 + YAML)
|
|
97
|
+
- Workflows are portable—same YAML runs on any infrastructure
|
|
98
|
+
- Child nodes can spawn sub-workflows
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 4. Backends
|
|
103
|
+
|
|
104
|
+
**What:** Pluggable persistence implementations.
|
|
105
|
+
|
|
106
|
+
**Purpose:** Store state, workflows, telemetry, and history. Infrastructure-agnostic.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from soe.local_backends import create_in_memory_backends
|
|
110
|
+
|
|
111
|
+
backends = create_in_memory_backends()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**The 5 Backend Types:**
|
|
115
|
+
|
|
116
|
+
| Backend | Interface | Purpose |
|
|
117
|
+
|---------|-----------|---------|
|
|
118
|
+
| `context` | `ContextBackend` | Execution state |
|
|
119
|
+
| `workflow` | `WorkflowBackend` | Workflow definitions |
|
|
120
|
+
| `telemetry` | `TelemetryBackend` | Logs and events |
|
|
121
|
+
| `conversation_history` | `ConversationHistoryBackend` | LLM chat history |
|
|
122
|
+
| `schema` | `SchemaBackend` | Context validation schemas |
|
|
123
|
+
|
|
124
|
+
**Key Properties:**
|
|
125
|
+
- Follow Python `Protocol` (structural typing)
|
|
126
|
+
- Built-in: In-Memory, Local Files
|
|
127
|
+
- Custom: PostgreSQL, DynamoDB, Redis, etc.
|
|
128
|
+
|
|
129
|
+
See [Chapter 10: Infrastructure](../guide_10_infrastructure.md) for implementation details.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 5. Nodes
|
|
134
|
+
|
|
135
|
+
**What:** Execution units that perform work when triggered by signals.
|
|
136
|
+
|
|
137
|
+
**Purpose:** The building blocks of workflows. Each node type has a specific role.
|
|
138
|
+
|
|
139
|
+
**The 3 Core Node Types:**
|
|
140
|
+
|
|
141
|
+
| Node Type | Purpose | Does Work? |
|
|
142
|
+
|-----------|---------|-----------|
|
|
143
|
+
| `tool` | Execute Python functions | ✅ Yes |
|
|
144
|
+
| `llm` | Generate text via LLM | ✅ Yes |
|
|
145
|
+
| `router` | Evaluate conditions, emit signals | ❌ Pure routing |
|
|
146
|
+
|
|
147
|
+
With these three core nodes, you can build any workflow pattern—including custom agent loops.
|
|
148
|
+
|
|
149
|
+
**Additional Node Types (Convenience):**
|
|
150
|
+
|
|
151
|
+
| Node Type | Purpose | Description |
|
|
152
|
+
|-----------|---------|-------------|
|
|
153
|
+
| `agent` | Autonomous reasoning with tools | Built-in ReAct loop |
|
|
154
|
+
| `child` | Spawn sub-workflows | Sub-orchestration |
|
|
155
|
+
|
|
156
|
+
**Key Properties:**
|
|
157
|
+
- Nodes are stateless—all state lives in Context
|
|
158
|
+
- Nodes communicate only via Signals
|
|
159
|
+
- Nodes are created by factory functions
|
|
160
|
+
- Multiple instances of the same type can exist
|
|
161
|
+
|
|
162
|
+
```yaml
|
|
163
|
+
ValidateUser:
|
|
164
|
+
node_type: router
|
|
165
|
+
event_triggers: [START]
|
|
166
|
+
...
|
|
167
|
+
|
|
168
|
+
ValidatePayment:
|
|
169
|
+
node_type: router
|
|
170
|
+
event_triggers: [USER_VALID]
|
|
171
|
+
...
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## 6. Callers
|
|
177
|
+
|
|
178
|
+
**What:** Functions that control how signals are broadcast.
|
|
179
|
+
|
|
180
|
+
**Purpose:** The communication layer. Enables distribution without changing workflows.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
def broadcast_signals_caller(id: str, signals: List[str]) -> None:
|
|
184
|
+
broadcast_signals(id, signals, nodes, backends)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Key Properties:**
|
|
188
|
+
- The default caller is synchronous and in-process
|
|
189
|
+
- Custom callers enable distribution (HTTP, Lambda, SQS, etc.)
|
|
190
|
+
- Callers wrap `broadcast_signals` with additional behavior
|
|
191
|
+
- The same workflow works with any caller
|
|
192
|
+
|
|
193
|
+
**Distribution Examples:**
|
|
194
|
+
- **HTTP Caller:** Signals become webhook POSTs
|
|
195
|
+
- **Lambda Caller:** Signals trigger AWS Lambda functions
|
|
196
|
+
- **SQS Caller:** Signals become queue messages
|
|
197
|
+
|
|
198
|
+
See [Chapter 10: Infrastructure](../guide_10_infrastructure.md) for implementation details.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## 6. Identities
|
|
203
|
+
|
|
204
|
+
**What:** System prompts for LLM and Agent nodes.
|
|
205
|
+
|
|
206
|
+
**Purpose:** Define roles and consistent behavior for LLM calls without repeating in every prompt.
|
|
207
|
+
|
|
208
|
+
```yaml
|
|
209
|
+
identities:
|
|
210
|
+
analyst: |
|
|
211
|
+
You are a senior data analyst. Be thorough and precise.
|
|
212
|
+
Always cite sources when making claims.
|
|
213
|
+
reviewer: |
|
|
214
|
+
You are a code reviewer. Focus on correctness and maintainability.
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Key Properties:**
|
|
218
|
+
- Defined in config YAML alongside workflows
|
|
219
|
+
- Referenced by `identity` field in LLM/Agent nodes
|
|
220
|
+
- Stored by `IdentityBackend`
|
|
221
|
+
- Keyed by `main_execution_id` (child workflows access parent's identities)
|
|
222
|
+
- Removes the need to specify role in every prompt
|
|
223
|
+
|
|
224
|
+
**Usage in Nodes:**
|
|
225
|
+
```yaml
|
|
226
|
+
Analyzer:
|
|
227
|
+
node_type: llm
|
|
228
|
+
identity: analyst # References identity defined above
|
|
229
|
+
prompt: "Analyze: {{ context.input }}"
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
See [Chapter 7: Identity](../guide_07_identity.md) for details.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## 7. Context Schema
|
|
237
|
+
|
|
238
|
+
**What:** Type definitions for context fields.
|
|
239
|
+
|
|
240
|
+
**Purpose:** Validate LLM output matches expected types before downstream use.
|
|
241
|
+
|
|
242
|
+
```yaml
|
|
243
|
+
context_schema:
|
|
244
|
+
summary:
|
|
245
|
+
type: string
|
|
246
|
+
description: A one-sentence summary
|
|
247
|
+
result:
|
|
248
|
+
type: object
|
|
249
|
+
description: The workflow result
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Key Properties:**
|
|
253
|
+
- Defined in config YAML alongside workflows
|
|
254
|
+
- Validates LLM `output_field` values
|
|
255
|
+
- Stored by `ContextSchemaBackend`
|
|
256
|
+
- Keyed by `main_execution_id` (child workflows access parent's schema)
|
|
257
|
+
- Removes the need to specify output format in every prompt
|
|
258
|
+
|
|
259
|
+
**Available Types:**
|
|
260
|
+
| Type | Python Type |
|
|
261
|
+
|------|-------------|
|
|
262
|
+
| `string` | `str` |
|
|
263
|
+
| `integer` | `int` |
|
|
264
|
+
| `number` | `float` |
|
|
265
|
+
| `boolean` | `bool` |
|
|
266
|
+
| `object` | `dict` |
|
|
267
|
+
| `list` | `list` |
|
|
268
|
+
|
|
269
|
+
See [Chapter 6: Context Schema](../guide_06_schema.md) for details.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## How Primitives Interact
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
277
|
+
│ CONFIG (YAML) │
|
|
278
|
+
│ workflows, identities, context_schema │
|
|
279
|
+
└─────────────────────────────────────────────────────────────┘
|
|
280
|
+
│
|
|
281
|
+
▼
|
|
282
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
283
|
+
│ ORCHESTRATION │
|
|
284
|
+
│ Parses config, initializes backends, broadcasts signals │
|
|
285
|
+
└─────────────────────────────────────────────────────────────┘
|
|
286
|
+
│
|
|
287
|
+
▼
|
|
288
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
289
|
+
│ SIGNALS │
|
|
290
|
+
│ Flow through the system: START → VALID → COMPLETE │
|
|
291
|
+
└─────────────────────────────────────────────────────────────┘
|
|
292
|
+
│
|
|
293
|
+
▼
|
|
294
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
295
|
+
│ NODES │
|
|
296
|
+
│ Triggered by signals, read/write Context, emit signals │
|
|
297
|
+
│ router | tool | llm | agent | child │
|
|
298
|
+
│ LLM/Agent nodes use: Identities + Context Schema │
|
|
299
|
+
└─────────────────────────────────────────────────────────────┘
|
|
300
|
+
│
|
|
301
|
+
▼
|
|
302
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
303
|
+
│ CONTEXT │
|
|
304
|
+
│ Shared state: {"user": "alice", "result": "..."} │
|
|
305
|
+
│ Accessible via Jinja: {{ context.user }} │
|
|
306
|
+
└─────────────────────────────────────────────────────────────┘
|
|
307
|
+
│
|
|
308
|
+
▼
|
|
309
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
310
|
+
│ BACKENDS │
|
|
311
|
+
│ context | workflow | identity | context_schema | telemetry │
|
|
312
|
+
└─────────────────────────────────────────────────────────────┘
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
See [Chapter 9: Ecosystem](../guide_09_ecosystem.md) for multi-workflow patterns.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Summary Table
|
|
320
|
+
|
|
321
|
+
| Primitive | What | Where Defined | Stored By |
|
|
322
|
+
|-----------|------|---------------|-----------|
|
|
323
|
+
| **Signals** | Named events | YAML `event_emissions` | — (transient) |
|
|
324
|
+
| **Context** | Shared state | Runtime | `ContextBackend` |
|
|
325
|
+
| **Workflows** | Node blueprints | YAML files | `WorkflowBackend` |
|
|
326
|
+
| **Backends** | Persistence | Python classes | — |
|
|
327
|
+
| **Nodes** | Execution units | YAML + factories | — |
|
|
328
|
+
| **Identities** | System prompts | Config YAML | `IdentityBackend` |
|
|
329
|
+
| **Context Schema** | Type validation | Config YAML | `ContextSchemaBackend` |
|
|
330
|
+
|
|
331
|
+
Master these 7 primitives, and you can build anything with SOE.
|