soe-ai 0.1.2__py3-none-any.whl → 0.1.4__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 (36) hide show
  1. soe/broker.py +14 -32
  2. soe/builtin_tools/__init__.py +12 -0
  3. soe/builtin_tools/soe_get_context_schema.py +56 -0
  4. soe/builtin_tools/soe_get_identities.py +63 -0
  5. soe/builtin_tools/soe_inject_context_schema_field.py +80 -0
  6. soe/builtin_tools/soe_inject_identity.py +64 -0
  7. soe/builtin_tools/soe_remove_context_schema_field.py +61 -0
  8. soe/builtin_tools/soe_remove_identity.py +61 -0
  9. soe/builtin_tools/soe_update_context.py +5 -2
  10. soe/docs/builtins/context_schema.md +158 -0
  11. soe/docs/builtins/identity.md +139 -0
  12. soe/docs/guide_01_tool.md +52 -0
  13. soe/docs/guide_02_llm.md +5 -5
  14. soe/docs/guide_11_builtins.md +11 -3
  15. soe/docs/index.md +3 -1
  16. soe/docs/primitives/node_reference.md +1 -0
  17. soe/docs/primitives/signals.md +10 -9
  18. soe/docs_index.py +1 -1
  19. soe/init.py +7 -1
  20. soe/lib/inheritance.py +42 -1
  21. soe/nodes/agent/factory.py +1 -1
  22. soe/nodes/agent/stages/response.py +3 -3
  23. soe/nodes/agent/types.py +1 -1
  24. soe/nodes/lib/response_builder.py +14 -12
  25. soe/nodes/lib/signal_emission.py +6 -10
  26. soe/nodes/llm/factory.py +3 -3
  27. soe/nodes/tool/state.py +21 -1
  28. soe/nodes/tool/validation/config.py +15 -0
  29. soe/validation/__init__.py +7 -1
  30. soe/validation/config.py +22 -0
  31. {soe_ai-0.1.2.dist-info → soe_ai-0.1.4.dist-info}/METADATA +3 -3
  32. {soe_ai-0.1.2.dist-info → soe_ai-0.1.4.dist-info}/RECORD +36 -28
  33. {soe_ai-0.1.2.dist-info → soe_ai-0.1.4.dist-info}/WHEEL +1 -1
  34. /soe/docs/builtins/{explore_docs.md → soe_explore_docs.md} +0 -0
  35. {soe_ai-0.1.2.dist-info → soe_ai-0.1.4.dist-info}/licenses/LICENSE +0 -0
  36. {soe_ai-0.1.2.dist-info → soe_ai-0.1.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,139 @@
1
+
2
+ # Built-in: Identity Management
3
+
4
+ ## Runtime Identity Control
5
+
6
+ These built-in tools enable workflows to **manage identities at runtime**. Identities define the system prompts for LLM personas—querying, adding, and removing them enables dynamic personality switching and self-evolving agents.
7
+
8
+ ---
9
+
10
+ ## Available Tools
11
+
12
+ | Tool | Purpose |
13
+ |------|---------|
14
+ | `soe_get_identities` | Query current identity definitions |
15
+ | `soe_inject_identity` | Add or update an identity |
16
+ | `soe_remove_identity` | Remove an identity |
17
+
18
+ ---
19
+
20
+ ## soe_get_identities
21
+
22
+ Query identity definitions from the current execution.
23
+
24
+ ### Parameters
25
+
26
+ | Parameter | Type | Default | Description |
27
+ |-----------|------|---------|-------------|
28
+ | `identity_name` | `Optional[str]` | `None` | Get a specific identity |
29
+ | `list_only` | `bool` | `False` | Return only identity names |
30
+
31
+ ### Return Values
32
+
33
+ - **No parameters**: Returns full dict of `identity_name -> system_prompt`
34
+ - **`list_only=True`**: Returns `{"identity_names": ["assistant", "expert", ...]}`
35
+ - **`identity_name` provided**: Returns `{"identity_name": "...", "system_prompt": "..."}`
36
+ - **Not found**: Returns `{"error": "...", "available": [...]}`
37
+
38
+ ### Use Cases
39
+
40
+ - **Introspection** — Let LLMs see available personas
41
+ - **Decision making** — Choose identity based on task
42
+ - **Debugging** — Inspect identity configuration
43
+
44
+ ---
45
+
46
+ ## soe_inject_identity
47
+
48
+ Add or update a single identity definition.
49
+
50
+ ### Parameters
51
+
52
+ | Parameter | Type | Description |
53
+ |-----------|------|-------------|
54
+ | `identity_name` | `str` | Name/key for the identity |
55
+ | `system_prompt` | `str` | The system prompt text |
56
+
57
+ ### Return Value
58
+
59
+ ```python
60
+ {
61
+ "success": True,
62
+ "identity_name": "expert",
63
+ "action": "created", # or "updated"
64
+ "message": "Successfully created identity 'expert'"
65
+ }
66
+ ```
67
+
68
+ ### Use Cases
69
+
70
+ - **Dynamic personas** — Create identities based on user needs
71
+ - **Self-evolution** — Workflows define their own personas
72
+ - **Specialization** — Add domain-specific identities at runtime
73
+
74
+ ---
75
+
76
+ ## soe_remove_identity
77
+
78
+ Remove a single identity definition.
79
+
80
+ ### Parameters
81
+
82
+ | Parameter | Type | Description |
83
+ |-----------|------|-------------|
84
+ | `identity_name` | `str` | Name of identity to remove |
85
+
86
+ ### Return Value
87
+
88
+ ```python
89
+ {
90
+ "removed": True,
91
+ "identity_name": "old_persona",
92
+ "message": "Successfully removed identity 'old_persona'"
93
+ }
94
+ ```
95
+
96
+ Raises `ValueError` if identity not found.
97
+
98
+ ### Use Cases
99
+
100
+ - **Cleanup** — Remove unused personas
101
+ - **Reset** — Clear identities before reconfiguration
102
+ - **Evolution** — Replace outdated personas
103
+
104
+ ---
105
+
106
+ ## Identity Patterns
107
+
108
+ ### Dynamic Persona Selection
109
+
110
+ A workflow that chooses its identity based on task:
111
+
112
+
113
+ ```yaml
114
+ dynamic_persona:
115
+ AnalyzeTask:
116
+ node_type: llm
117
+ event_triggers: [START]
118
+ prompt: "Analyze this task and suggest a persona: {{ context.task }}"
119
+ output_field: suggested_persona
120
+ event_emissions:
121
+ - signal_name: PERSONA_SUGGESTED
122
+
123
+ CreatePersona:
124
+ node_type: tool
125
+ event_triggers: [PERSONA_SUGGESTED]
126
+ tool_name: soe_inject_identity
127
+ context_parameter_field: persona_config
128
+ output_field: identity_result
129
+ event_emissions:
130
+ - signal_name: PERSONA_READY
131
+ ```
132
+
133
+
134
+ ---
135
+
136
+ ## Related
137
+
138
+ - [Built-in Tools Overview](../guide_11_builtins.md) — All available built-ins
139
+ - [Identity Guide](../guide_07_identity.md) — Identity fundamentals
soe/docs/guide_01_tool.md CHANGED
@@ -39,6 +39,58 @@ example_workflow:
39
39
  3. **`output_field`**: Where to store the result in context.
40
40
  4. **`event_emissions`**: Signals to emit after execution (conditions evaluate `result`).
41
41
 
42
+ ## Passing Parameters to Tools
43
+
44
+ There are two ways to pass parameters to a tool: **inline parameters** (hardcoded in YAML) or **context parameters** (dynamic from context).
45
+
46
+ ### Option 1: Inline Parameters (Static)
47
+
48
+ Use `parameters` to specify tool arguments directly in the workflow YAML:
49
+
50
+ ```yaml
51
+ example_workflow:
52
+ ReadToolDocs:
53
+ node_type: tool
54
+ event_triggers: [START]
55
+ tool_name: soe_explore_docs
56
+ parameters:
57
+ path: "soe/docs/guide_01_tool.md"
58
+ action: "read"
59
+ output_field: tool_documentation
60
+ event_emissions:
61
+ - signal_name: DOCS_READY
62
+ ```
63
+
64
+ **Jinja templates work in parameters:**
65
+
66
+ ```yaml
67
+ example_workflow:
68
+ FetchUserData:
69
+ node_type: tool
70
+ event_triggers: [START]
71
+ tool_name: fetch_data
72
+ parameters:
73
+ user_id: "{{ context.current_user_id }}"
74
+ include_history: true
75
+ output_field: user_data
76
+ event_emissions:
77
+ - signal_name: DATA_FETCHED
78
+ ```
79
+
80
+ ### Option 2: Context Parameters (Dynamic)
81
+
82
+ Use `context_parameter_field` when the parameters come from another node's output or initial context:
83
+
84
+ ```yaml
85
+ example_workflow:
86
+ SendEmail:
87
+ node_type: tool
88
+ event_triggers: [START]
89
+ tool_name: send_email
90
+ context_parameter_field: email_data
91
+ output_field: email_result
92
+ ```
93
+
42
94
  ### Understanding context_parameter_field
43
95
 
44
96
  The `context_parameter_field` specifies which context field contains the parameters to pass to your tool. This field must contain a dictionary that will be unpacked as keyword arguments.
soe/docs/guide_02_llm.md CHANGED
@@ -79,7 +79,7 @@ This pattern is incredibly useful for:
79
79
 
80
80
  ## LLM Signal Selection (Resolution Step)
81
81
 
82
- When an LLM node has multiple signals with **conditions** (plain text, not Jinja), the LLM itself decides which signal to emit.
82
+ When an LLM node has multiple signals with **conditions** (plain text, not Jinja), the LLM itself decides which signals to emit.
83
83
 
84
84
  ### The Workflow
85
85
 
@@ -103,8 +103,8 @@ example_workflow:
103
103
 
104
104
  1. The LLM analyzes the sentiment
105
105
  2. SOE sees multiple signals with plain-text conditions (no `{{ }}`)
106
- 3. SOE asks the LLM: "Based on your analysis, which signal should be emitted?" using the conditions as descriptions
107
- 4. The LLM returns the appropriate signal (POSITIVE, NEGATIVE, or NEUTRAL)
106
+ 3. SOE asks the LLM: "Select ALL signals that apply" using the conditions as descriptions
107
+ 4. The LLM returns a list of matching signals (can be none, one, or multiple)
108
108
 
109
109
  This is called the **resolution step** - it lets the LLM make routing decisions based on its understanding.
110
110
 
@@ -115,7 +115,7 @@ The `condition` field controls how signals are emitted:
115
115
  | Condition | Behavior |
116
116
  |-----------|----------|
117
117
  | **No condition** | Signal is always emitted |
118
- | **Plain text** | Semantic—LLM selects which signal to emit based on the description |
118
+ | **Plain text** | Semantic—LLM selects any/all signals that apply based on the descriptions |
119
119
  | **Jinja template (`{{ }}`)** | Programmatic—evaluated against `context`, emits if truthy |
120
120
 
121
121
  **How SOE decides:**
@@ -123,7 +123,7 @@ The `condition` field controls how signals are emitted:
123
123
  1. **No conditions**: All signals emit unconditionally after node execution
124
124
  2. **Has conditions**: SOE checks if they contain `{{ }}`:
125
125
  - **Yes (Jinja)**: Evaluate expression against `context`—emit if result is truthy
126
- - **No (plain text)**: Ask LLM to choose which signal best matches its output
126
+ - **No (plain text)**: Ask LLM to select which signals apply (multi-select)
127
127
 
128
128
  ## Testing LLM Nodes
129
129
 
@@ -57,19 +57,25 @@ Unlike custom tools that need a `tools_registry`, built-ins work immediately. SO
57
57
 
58
58
  | Built-in | Purpose | Documentation |
59
59
  |----------|---------|---------------|
60
- | `soe_explore_docs` | Make SOE self-aware by exploring its documentation | [explore_docs](builtins/explore_docs.md) |
60
+ | `soe_explore_docs` | Make SOE self-aware by exploring its documentation | [explore_docs](builtins/soe_explore_docs.md) |
61
61
  | `soe_get_workflows` | Query registered workflow definitions | [workflows](builtins/workflows.md) |
62
62
  | `soe_inject_workflow` | Add new workflows at runtime | [workflows](builtins/workflows.md) |
63
63
  | `soe_inject_node` | Add or modify nodes in workflows | [workflows](builtins/workflows.md) |
64
64
  | `soe_remove_workflow` | Remove workflows from registry | [workflows](builtins/workflows.md) |
65
65
  | `soe_remove_node` | Remove nodes from workflows | [workflows](builtins/workflows.md) |
66
+ | `soe_add_signal` | Add signals to node configurations | [workflows](builtins/workflows.md) |
66
67
  | `soe_get_context` | Read execution context | [context](builtins/context.md) |
67
68
  | `soe_update_context` | Modify execution context | [context](builtins/context.md) |
68
69
  | `soe_copy_context` | Clone context for parallel execution | [context](builtins/context.md) |
69
70
  | `soe_list_contexts` | Discover available contexts | [context](builtins/context.md) |
71
+ | `soe_get_identities` | Query identity definitions | [identity](builtins/identity.md) |
72
+ | `soe_inject_identity` | Add or update an identity | [identity](builtins/identity.md) |
73
+ | `soe_remove_identity` | Remove an identity | [identity](builtins/identity.md) |
74
+ | `soe_get_context_schema` | Query context schema | [context_schema](builtins/context_schema.md) |
75
+ | `soe_inject_context_schema_field` | Add or update a schema field | [context_schema](builtins/context_schema.md) |
76
+ | `soe_remove_context_schema_field` | Remove a schema field | [context_schema](builtins/context_schema.md) |
70
77
  | `soe_get_available_tools` | List all registered tools | [tools](builtins/tools.md) |
71
78
  | `soe_call_tool` | Invoke a tool by name dynamically | [tools](builtins/tools.md) |
72
- | `soe_add_signal` | Emit signals programmatically | [workflows](builtins/workflows.md) |
73
79
 
74
80
  ---
75
81
 
@@ -105,9 +111,11 @@ This pattern is the foundation for metacognitive workflows that can reason about
105
111
 
106
112
  Explore each built-in category in detail:
107
113
 
108
- - **[explore_docs](builtins/explore_docs.md)** — Self-awareness through documentation
114
+ - **[explore_docs](builtins/soe_explore_docs.md)** — Self-awareness through documentation
109
115
  - **[workflows](builtins/workflows.md)** — Runtime workflow modification
110
116
  - **[context](builtins/context.md)** — Execution state management
117
+ - **[identity](builtins/identity.md)** — Runtime identity management
118
+ - **[context_schema](builtins/context_schema.md)** — Runtime schema management
111
119
  - **[tools](builtins/tools.md)** — Dynamic tool discovery and invocation
112
120
 
113
121
  ---
soe/docs/index.md CHANGED
@@ -81,10 +81,12 @@ Built-in tools enable granular runtime modifications:
81
81
 
82
82
  | Built-in | Description |
83
83
  |----------|-------------|
84
- | [explore_docs](builtins/explore_docs.md) | Make SOE self-aware by exploring its documentation |
84
+ | [soe_explore_docs](builtins/soe_explore_docs.md) | Make SOE self-aware by exploring its documentation |
85
85
  | [workflows](builtins/workflows.md) | Query, inject, and modify workflows at runtime |
86
86
  | [context](builtins/context.md) | Read, update, and copy execution context |
87
87
  | [tools](builtins/tools.md) | Discover and dynamically call registered tools |
88
+ | [identity](builtins/identity.md) | Query, inject, and remove identity definitions |
89
+ | [context_schema](builtins/context_schema.md) | Query, inject, and remove context schema fields |
88
90
 
89
91
  ---
90
92
 
@@ -44,6 +44,7 @@ Complete reference for all node configuration parameters across all node types.
44
44
  |-----------|------|--------|-----|-------|------|-------|-------------|
45
45
  | `tool_name` | `str` | ✗ | ✗ | ✗ | **R** | ✗ | Tool to execute from registry |
46
46
  | `tools` | `List[str]` | ✗ | ✗ | **O** | ✗ | ✗ | Tool names available to agent |
47
+ | `parameters` | `Dict` | ✗ | ✗ | ✗ | **O** | ✗ | Inline tool kwargs (supports Jinja) |
47
48
  | `context_parameter_field` | `str` | ✗ | ✗ | ✗ | **O** | ✗ | Context field containing tool kwargs |
48
49
 
49
50
  ### Child Workflow Parameters
@@ -122,7 +122,7 @@ example_workflow:
122
122
 
123
123
  Both `PROCESSING_DONE` and `LOG_EVENT` emit every time the node runs.
124
124
 
125
- > **Note**: For Router nodes, multiple unconditional signals all emit simultaneously (fan-out pattern). For LLM/Agent nodes with multiple signals, the LLM must select one - use Jinja conditions like `{{ true }}` if you want all signals to emit.
125
+ > **Note**: For Router nodes, multiple unconditional signals all emit simultaneously (fan-out pattern). For LLM/Agent nodes with multiple signals, the LLM can select any/all that apply—including none.
126
126
 
127
127
  ### Mode 2: Jinja Template (Programmatic)
128
128
 
@@ -207,8 +207,9 @@ The behavior depends on the node type:
207
207
  │ └─ Zero signals? → Nothing emitted │
208
208
  │ └─ Single signal? → Emit unconditionally │
209
209
  │ └─ Multiple signals? │
210
- │ └─ LLM selects ONE signal
210
+ │ └─ LLM selects ANY/ALL that apply
211
211
  │ (uses conditions as semantic descriptions) │
212
+ │ (can select none, one, or multiple) │
212
213
  │ │
213
214
  └─────────────────────────────────────────────────────────────┘
214
215
 
@@ -300,7 +301,7 @@ example_workflow:
300
301
  condition: "The message is factual, neutral, or emotionally ambiguous"
301
302
  ```
302
303
 
303
- **LLM Selection Mechanism**: SOE adds a `selected_signal` field to the response model, forcing the LLM to choose from the options. The condition text serves as the description.
304
+ **LLM Selection Mechanism**: SOE adds a `selected_signals` field to the response model, allowing the LLM to select any/all signals that apply. The condition text serves as the description.
304
305
 
305
306
  ---
306
307
 
@@ -627,26 +628,26 @@ When the child updates these keys, they're automatically copied to the parent's
627
628
 
628
629
  ## LLM Signal Selection: Under the Hood
629
630
 
630
- When the LLM selects a signal, SOE:
631
+ When the LLM selects signals, SOE:
631
632
 
632
- 1. **Builds a response model** with a `selected_signal` field:
633
+ 1. **Builds a response model** with a `selected_signals` field (list):
633
634
  ```python
634
635
  class Response(BaseModel):
635
636
  response: str
636
- selected_signal: Literal["POSITIVE", "NEGATIVE", "NEUTRAL"]
637
+ selected_signals: List[Literal["POSITIVE", "NEGATIVE", "NEUTRAL"]] = []
637
638
  ```
638
639
 
639
640
  2. **Provides descriptions** from the `condition` field:
640
641
  ```
641
- Select one of these signals based on your response:
642
+ Select ALL signals that apply (can be none, one, or multiple):
642
643
  - POSITIVE: The message expresses positive sentiment
643
644
  - NEGATIVE: The message expresses negative sentiment
644
645
  - NEUTRAL: The message is neutral
645
646
  ```
646
647
 
647
- 3. **Extracts the selection** and emits that signal.
648
+ 3. **Extracts the selection** and emits all selected signals (can be empty).
648
649
 
649
- This is why plain-text conditions are called "semantic"—the LLM understands the description and makes a judgment call.
650
+ This is why plain-text conditions are called "semantic"—the LLM understands the descriptions and selects all that apply.
650
651
 
651
652
  ---
652
653