soe-ai 0.1.0__tar.gz → 0.1.2__tar.gz

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 (147) hide show
  1. {soe_ai-0.1.0 → soe_ai-0.1.2}/PKG-INFO +72 -9
  2. {soe_ai-0.1.0 → soe_ai-0.1.2}/README.md +70 -7
  3. {soe_ai-0.1.0 → soe_ai-0.1.2}/pyproject.toml +6 -5
  4. {soe_ai-0.1.0 → soe_ai-0.1.2}/soe/broker.py +4 -5
  5. soe_ai-0.1.2/soe/builtin_tools/__init__.py +39 -0
  6. soe_ai-0.1.2/soe/builtin_tools/soe_add_signal.py +82 -0
  7. soe_ai-0.1.2/soe/builtin_tools/soe_call_tool.py +111 -0
  8. soe_ai-0.1.2/soe/builtin_tools/soe_copy_context.py +80 -0
  9. soe_ai-0.1.2/soe/builtin_tools/soe_explore_docs.py +290 -0
  10. soe_ai-0.1.2/soe/builtin_tools/soe_get_available_tools.py +42 -0
  11. soe_ai-0.1.2/soe/builtin_tools/soe_get_context.py +50 -0
  12. soe_ai-0.1.2/soe/builtin_tools/soe_get_workflows.py +63 -0
  13. soe_ai-0.1.2/soe/builtin_tools/soe_inject_node.py +86 -0
  14. soe_ai-0.1.2/soe/builtin_tools/soe_inject_workflow.py +105 -0
  15. soe_ai-0.1.2/soe/builtin_tools/soe_list_contexts.py +73 -0
  16. soe_ai-0.1.2/soe/builtin_tools/soe_remove_node.py +72 -0
  17. soe_ai-0.1.2/soe/builtin_tools/soe_remove_workflow.py +62 -0
  18. soe_ai-0.1.2/soe/builtin_tools/soe_update_context.py +54 -0
  19. soe_ai-0.1.2/soe/docs/_config.yml +10 -0
  20. soe_ai-0.1.2/soe/docs/advanced_patterns/guide_fanout_and_aggregations.md +318 -0
  21. soe_ai-0.1.2/soe/docs/advanced_patterns/guide_inheritance.md +435 -0
  22. soe_ai-0.1.2/soe/docs/advanced_patterns/hybrid_intelligence.md +237 -0
  23. soe_ai-0.1.2/soe/docs/advanced_patterns/index.md +49 -0
  24. soe_ai-0.1.2/soe/docs/advanced_patterns/operational.md +781 -0
  25. soe_ai-0.1.2/soe/docs/advanced_patterns/self_evolving_workflows.md +385 -0
  26. soe_ai-0.1.2/soe/docs/advanced_patterns/swarm_intelligence.md +211 -0
  27. soe_ai-0.1.2/soe/docs/builtins/context.md +164 -0
  28. soe_ai-0.1.2/soe/docs/builtins/explore_docs.md +135 -0
  29. soe_ai-0.1.2/soe/docs/builtins/tools.md +164 -0
  30. soe_ai-0.1.2/soe/docs/builtins/workflows.md +199 -0
  31. soe_ai-0.1.2/soe/docs/guide_00_getting_started.md +341 -0
  32. soe_ai-0.1.2/soe/docs/guide_01_tool.md +206 -0
  33. soe_ai-0.1.2/soe/docs/guide_02_llm.md +143 -0
  34. soe_ai-0.1.2/soe/docs/guide_03_router.md +146 -0
  35. soe_ai-0.1.2/soe/docs/guide_04_patterns.md +475 -0
  36. soe_ai-0.1.2/soe/docs/guide_05_agent.md +159 -0
  37. soe_ai-0.1.2/soe/docs/guide_06_schema.md +397 -0
  38. soe_ai-0.1.2/soe/docs/guide_07_identity.md +540 -0
  39. soe_ai-0.1.2/soe/docs/guide_08_child.md +612 -0
  40. soe_ai-0.1.2/soe/docs/guide_09_ecosystem.md +690 -0
  41. soe_ai-0.1.2/soe/docs/guide_10_infrastructure.md +427 -0
  42. soe_ai-0.1.2/soe/docs/guide_11_builtins.md +118 -0
  43. soe_ai-0.1.2/soe/docs/index.md +104 -0
  44. soe_ai-0.1.2/soe/docs/primitives/backends.md +281 -0
  45. soe_ai-0.1.2/soe/docs/primitives/context.md +256 -0
  46. soe_ai-0.1.2/soe/docs/primitives/node_reference.md +259 -0
  47. soe_ai-0.1.2/soe/docs/primitives/primitives.md +331 -0
  48. soe_ai-0.1.2/soe/docs/primitives/signals.md +865 -0
  49. soe_ai-0.1.2/soe/docs_index.py +2 -0
  50. {soe_ai-0.1.0 → soe_ai-0.1.2}/soe/init.py +2 -2
  51. soe_ai-0.1.2/soe/lib/__init__.py +0 -0
  52. soe_ai-0.1.2/soe/lib/child_context.py +46 -0
  53. soe_ai-0.1.2/soe/lib/context_fields.py +51 -0
  54. soe_ai-0.1.2/soe/lib/inheritance.py +172 -0
  55. soe_ai-0.1.2/soe/lib/jinja_render.py +113 -0
  56. soe_ai-0.1.2/soe/lib/operational.py +51 -0
  57. soe_ai-0.1.2/soe/lib/parent_sync.py +71 -0
  58. soe_ai-0.1.2/soe/lib/register_event.py +75 -0
  59. soe_ai-0.1.2/soe/lib/schema_validation.py +134 -0
  60. soe_ai-0.1.2/soe/lib/yaml_parser.py +14 -0
  61. soe_ai-0.1.2/soe/local_backends/__init__.py +18 -0
  62. soe_ai-0.1.2/soe/local_backends/factory.py +124 -0
  63. soe_ai-0.1.2/soe/local_backends/in_memory/context.py +38 -0
  64. soe_ai-0.1.2/soe/local_backends/in_memory/conversation_history.py +60 -0
  65. soe_ai-0.1.2/soe/local_backends/in_memory/identity.py +52 -0
  66. soe_ai-0.1.2/soe/local_backends/in_memory/schema.py +40 -0
  67. soe_ai-0.1.2/soe/local_backends/in_memory/telemetry.py +38 -0
  68. soe_ai-0.1.2/soe/local_backends/in_memory/workflow.py +33 -0
  69. soe_ai-0.1.2/soe/local_backends/storage/context.py +57 -0
  70. soe_ai-0.1.2/soe/local_backends/storage/conversation_history.py +82 -0
  71. soe_ai-0.1.2/soe/local_backends/storage/identity.py +118 -0
  72. soe_ai-0.1.2/soe/local_backends/storage/schema.py +96 -0
  73. soe_ai-0.1.2/soe/local_backends/storage/telemetry.py +72 -0
  74. soe_ai-0.1.2/soe/local_backends/storage/workflow.py +56 -0
  75. soe_ai-0.1.2/soe/nodes/__init__.py +13 -0
  76. soe_ai-0.1.2/soe/nodes/agent/__init__.py +10 -0
  77. soe_ai-0.1.2/soe/nodes/agent/factory.py +134 -0
  78. soe_ai-0.1.2/soe/nodes/agent/lib/loop_handlers.py +150 -0
  79. soe_ai-0.1.2/soe/nodes/agent/lib/loop_state.py +157 -0
  80. soe_ai-0.1.2/soe/nodes/agent/lib/prompts.py +65 -0
  81. soe_ai-0.1.2/soe/nodes/agent/lib/tools.py +35 -0
  82. soe_ai-0.1.2/soe/nodes/agent/stages/__init__.py +12 -0
  83. soe_ai-0.1.2/soe/nodes/agent/stages/parameter.py +37 -0
  84. soe_ai-0.1.2/soe/nodes/agent/stages/response.py +54 -0
  85. soe_ai-0.1.2/soe/nodes/agent/stages/router.py +37 -0
  86. soe_ai-0.1.2/soe/nodes/agent/state.py +111 -0
  87. soe_ai-0.1.2/soe/nodes/agent/types.py +66 -0
  88. soe_ai-0.1.2/soe/nodes/agent/validation/__init__.py +11 -0
  89. soe_ai-0.1.2/soe/nodes/agent/validation/config.py +95 -0
  90. soe_ai-0.1.2/soe/nodes/agent/validation/operational.py +24 -0
  91. soe_ai-0.1.2/soe/nodes/child/__init__.py +3 -0
  92. soe_ai-0.1.2/soe/nodes/child/factory.py +61 -0
  93. soe_ai-0.1.2/soe/nodes/child/state.py +59 -0
  94. soe_ai-0.1.2/soe/nodes/child/validation/__init__.py +11 -0
  95. soe_ai-0.1.2/soe/nodes/child/validation/config.py +126 -0
  96. soe_ai-0.1.2/soe/nodes/child/validation/operational.py +28 -0
  97. soe_ai-0.1.2/soe/nodes/lib/conditions.py +71 -0
  98. soe_ai-0.1.2/soe/nodes/lib/context.py +24 -0
  99. soe_ai-0.1.2/soe/nodes/lib/conversation_history.py +77 -0
  100. soe_ai-0.1.2/soe/nodes/lib/identity.py +64 -0
  101. soe_ai-0.1.2/soe/nodes/lib/llm_resolver.py +142 -0
  102. soe_ai-0.1.2/soe/nodes/lib/output.py +68 -0
  103. soe_ai-0.1.2/soe/nodes/lib/response_builder.py +91 -0
  104. soe_ai-0.1.2/soe/nodes/lib/signal_emission.py +79 -0
  105. soe_ai-0.1.2/soe/nodes/lib/signals.py +54 -0
  106. soe_ai-0.1.2/soe/nodes/lib/tools.py +100 -0
  107. soe_ai-0.1.2/soe/nodes/llm/__init__.py +7 -0
  108. soe_ai-0.1.2/soe/nodes/llm/factory.py +103 -0
  109. soe_ai-0.1.2/soe/nodes/llm/state.py +76 -0
  110. soe_ai-0.1.2/soe/nodes/llm/types.py +12 -0
  111. soe_ai-0.1.2/soe/nodes/llm/validation/__init__.py +11 -0
  112. soe_ai-0.1.2/soe/nodes/llm/validation/config.py +89 -0
  113. soe_ai-0.1.2/soe/nodes/llm/validation/operational.py +23 -0
  114. soe_ai-0.1.2/soe/nodes/router/__init__.py +3 -0
  115. soe_ai-0.1.2/soe/nodes/router/factory.py +37 -0
  116. soe_ai-0.1.2/soe/nodes/router/state.py +32 -0
  117. soe_ai-0.1.2/soe/nodes/router/validation/__init__.py +11 -0
  118. soe_ai-0.1.2/soe/nodes/router/validation/config.py +58 -0
  119. soe_ai-0.1.2/soe/nodes/router/validation/operational.py +16 -0
  120. soe_ai-0.1.2/soe/nodes/tool/factory.py +66 -0
  121. soe_ai-0.1.2/soe/nodes/tool/lib/__init__.py +11 -0
  122. soe_ai-0.1.2/soe/nodes/tool/lib/conditions.py +35 -0
  123. soe_ai-0.1.2/soe/nodes/tool/lib/failure.py +28 -0
  124. soe_ai-0.1.2/soe/nodes/tool/lib/parameters.py +67 -0
  125. soe_ai-0.1.2/soe/nodes/tool/state.py +66 -0
  126. soe_ai-0.1.2/soe/nodes/tool/types.py +27 -0
  127. soe_ai-0.1.2/soe/nodes/tool/validation/__init__.py +15 -0
  128. soe_ai-0.1.2/soe/nodes/tool/validation/config.py +132 -0
  129. soe_ai-0.1.2/soe/nodes/tool/validation/operational.py +16 -0
  130. {soe_ai-0.1.0 → soe_ai-0.1.2}/soe/types.py +40 -28
  131. soe_ai-0.1.2/soe/validation/__init__.py +18 -0
  132. soe_ai-0.1.2/soe/validation/config.py +195 -0
  133. soe_ai-0.1.2/soe/validation/jinja.py +54 -0
  134. soe_ai-0.1.2/soe/validation/operational.py +110 -0
  135. {soe_ai-0.1.0 → soe_ai-0.1.2}/soe_ai.egg-info/PKG-INFO +72 -9
  136. soe_ai-0.1.2/soe_ai.egg-info/SOURCES.txt +142 -0
  137. {soe_ai-0.1.0 → soe_ai-0.1.2}/tests/test_local_storage_backends.py +3 -47
  138. soe_ai-0.1.0/soe/docs_index.py +0 -2
  139. soe_ai-0.1.0/soe/validation.py +0 -8
  140. soe_ai-0.1.0/soe_ai.egg-info/SOURCES.txt +0 -16
  141. {soe_ai-0.1.0 → soe_ai-0.1.2}/LICENSE +0 -0
  142. {soe_ai-0.1.0 → soe_ai-0.1.2}/setup.cfg +0 -0
  143. {soe_ai-0.1.0 → soe_ai-0.1.2}/soe/__init__.py +0 -0
  144. {soe_ai-0.1.0 → soe_ai-0.1.2}/soe_ai.egg-info/dependency_links.txt +0 -0
  145. {soe_ai-0.1.0 → soe_ai-0.1.2}/soe_ai.egg-info/requires.txt +0 -0
  146. {soe_ai-0.1.0 → soe_ai-0.1.2}/soe_ai.egg-info/top_level.txt +0 -0
  147. {soe_ai-0.1.0 → soe_ai-0.1.2}/tests/test_validation_errors.py +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soe-ai
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Signal-driven Orchestration Engine - Agent orchestration with event-driven workflow engine
5
- Author-email: Pedro Garcia <pedro@example.com>
5
+ Author-email: Pedro Garcia <pgarcia14180@gmail.com>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/pgarcia14180/soe
8
8
  Project-URL: Documentation, https://github.com/pgarcia14180/soe/tree/master/docs
@@ -91,12 +91,44 @@ All workflow state flows through **context**—a shared dictionary accessible vi
91
91
  - LLM prompts can interpolate any context field
92
92
  - No hidden state—everything is inspectable
93
93
 
94
- ### 3. Deterministic + Agentic
95
- Mix hard-coded logic with LLM-driven behavior in the same workflow. Router nodes are pure conditionals. Agent nodes can call tools. Use what you need.
94
+ ### 3. Purely Deterministic or Hybrid Agentic
95
+ SOE is a complete orchestration solution. You can use it as a purely deterministic engine for standard business logic, or mix in LLM-driven "Agentic" behavior.
96
+ - **Deterministic**: Use `router` and `tool` nodes for 100% predictable workflows.
97
+ - **Agentic**: Add `llm` and `agent` nodes for creative, reasoning-based tasks.
98
+ You get the safety of code with the flexibility of AI in a single, unified system.
96
99
 
97
100
  ### 4. Portable
98
101
  Workflows are YAML. Run them locally, in CI, in production. Extract them, version them, share them.
99
102
 
103
+ ### 5. Self-Evolving
104
+ Workflows can modify themselves at runtime. Built-in tools like `inject_workflow`, `inject_node_configuration`, and `add_signal` allow agents to:
105
+ - Create new workflows dynamically
106
+ - Add or modify nodes in existing workflows
107
+ - Update signal routing on the fly
108
+
109
+ This enables **meta-programming**: an AI system that can extend its own capabilities without human intervention.
110
+
111
+ ---
112
+
113
+ ## What SOE Unlocks
114
+
115
+ SOE is a **Protocol for Intelligence** that unlocks new forms of intelligent behavior:
116
+
117
+ ### Self-Evolving Intelligence
118
+ AI systems that can rewrite and improve themselves at runtime - the ultimate evolution of software.
119
+
120
+ ### Swarm Intelligence
121
+ Efficient collective decision-making among multiple agents through signal-based consensus.
122
+
123
+ ### Hybrid Intelligence
124
+ Seamless combination of deterministic logic and AI creativity with programmatic safety rails.
125
+
126
+ ### Fractal Intelligence
127
+ Hierarchical agent organizations that scale complexity while remaining manageable.
128
+
129
+ ### Infrastructure Intelligence
130
+ AI orchestration that works everywhere - from edge devices to cloud platforms.
131
+
100
132
  ---
101
133
 
102
134
  ## Installation
@@ -117,6 +149,37 @@ cd soe && uv sync
117
149
 
118
150
  ## Quick Start
119
151
 
152
+ ### 1. Provide Your LLM
153
+
154
+ SOE is LLM-agnostic. You must provide a `call_llm` function that matches this signature:
155
+
156
+ ```python
157
+ def call_llm(
158
+ prompt: str,
159
+ config: dict,
160
+ ) -> str:
161
+ """
162
+ Called by SOE when a node needs LLM processing.
163
+
164
+ Args:
165
+ prompt: The rendered prompt string (includes instructions, context, and schemas)
166
+ config: The full node configuration from YAML (useful for model parameters)
167
+
168
+ Returns:
169
+ The raw text response from the LLM.
170
+ """
171
+ # Example with OpenAI:
172
+ from openai import OpenAI
173
+ client = OpenAI()
174
+ response = client.chat.completions.create(
175
+ model=config.get("model", "gpt-4o"),
176
+ messages=[{"role": "user", "content": prompt}],
177
+ )
178
+ return response.choices[0].message.content
179
+ ```
180
+
181
+ ### 2. Run a Workflow
182
+
120
183
  ```python
121
184
  from soe import orchestrate, create_all_nodes
122
185
  from soe.local_backends import create_local_backends
@@ -134,8 +197,8 @@ example_workflow:
134
197
  # Create backends (storage for context, workflows, etc.)
135
198
  backends = create_local_backends("./data")
136
199
 
137
- # Create all node handlers
138
- nodes, broadcast = create_all_nodes(backends)
200
+ # Create all node handlers (pass your call_llm function)
201
+ nodes, broadcast = create_all_nodes(backends, call_llm=call_llm)
139
202
 
140
203
  # Run the workflow
141
204
  execution_id = orchestrate(
@@ -157,9 +220,9 @@ execution_id = orchestrate(
157
220
 
158
221
  | Audience | Start Here |
159
222
  |----------|------------|
160
- | **Builders** (workflow authors) | [Documentation](docs/index.md) — Step-by-step chapters |
161
- | **Engineers** (infrastructure) | [ARCHITECTURE.md](ai_docs/ARCHITECTURE.md) — Design philosophy |
162
- | **Researchers** (advanced patterns) | [Advanced Patterns](docs/advanced_patterns/index.md) — Swarm, hybrid, self-evolving |
223
+ | **Builders** (workflow authors) | [Documentation](soe/docs/index.md) — Step-by-step chapters |
224
+ | **Engineers** (infrastructure) | [Infrastructure Guide](soe/docs/guide_10_infrastructure.md) — Backend protocols |
225
+ | **Researchers** (advanced patterns) | [Advanced Patterns](docs/advanced_patterns/) — Swarm, hybrid, self-evolving |
163
226
 
164
227
  ---
165
228
 
@@ -55,12 +55,44 @@ All workflow state flows through **context**—a shared dictionary accessible vi
55
55
  - LLM prompts can interpolate any context field
56
56
  - No hidden state—everything is inspectable
57
57
 
58
- ### 3. Deterministic + Agentic
59
- Mix hard-coded logic with LLM-driven behavior in the same workflow. Router nodes are pure conditionals. Agent nodes can call tools. Use what you need.
58
+ ### 3. Purely Deterministic or Hybrid Agentic
59
+ SOE is a complete orchestration solution. You can use it as a purely deterministic engine for standard business logic, or mix in LLM-driven "Agentic" behavior.
60
+ - **Deterministic**: Use `router` and `tool` nodes for 100% predictable workflows.
61
+ - **Agentic**: Add `llm` and `agent` nodes for creative, reasoning-based tasks.
62
+ You get the safety of code with the flexibility of AI in a single, unified system.
60
63
 
61
64
  ### 4. Portable
62
65
  Workflows are YAML. Run them locally, in CI, in production. Extract them, version them, share them.
63
66
 
67
+ ### 5. Self-Evolving
68
+ Workflows can modify themselves at runtime. Built-in tools like `inject_workflow`, `inject_node_configuration`, and `add_signal` allow agents to:
69
+ - Create new workflows dynamically
70
+ - Add or modify nodes in existing workflows
71
+ - Update signal routing on the fly
72
+
73
+ This enables **meta-programming**: an AI system that can extend its own capabilities without human intervention.
74
+
75
+ ---
76
+
77
+ ## What SOE Unlocks
78
+
79
+ SOE is a **Protocol for Intelligence** that unlocks new forms of intelligent behavior:
80
+
81
+ ### Self-Evolving Intelligence
82
+ AI systems that can rewrite and improve themselves at runtime - the ultimate evolution of software.
83
+
84
+ ### Swarm Intelligence
85
+ Efficient collective decision-making among multiple agents through signal-based consensus.
86
+
87
+ ### Hybrid Intelligence
88
+ Seamless combination of deterministic logic and AI creativity with programmatic safety rails.
89
+
90
+ ### Fractal Intelligence
91
+ Hierarchical agent organizations that scale complexity while remaining manageable.
92
+
93
+ ### Infrastructure Intelligence
94
+ AI orchestration that works everywhere - from edge devices to cloud platforms.
95
+
64
96
  ---
65
97
 
66
98
  ## Installation
@@ -81,6 +113,37 @@ cd soe && uv sync
81
113
 
82
114
  ## Quick Start
83
115
 
116
+ ### 1. Provide Your LLM
117
+
118
+ SOE is LLM-agnostic. You must provide a `call_llm` function that matches this signature:
119
+
120
+ ```python
121
+ def call_llm(
122
+ prompt: str,
123
+ config: dict,
124
+ ) -> str:
125
+ """
126
+ Called by SOE when a node needs LLM processing.
127
+
128
+ Args:
129
+ prompt: The rendered prompt string (includes instructions, context, and schemas)
130
+ config: The full node configuration from YAML (useful for model parameters)
131
+
132
+ Returns:
133
+ The raw text response from the LLM.
134
+ """
135
+ # Example with OpenAI:
136
+ from openai import OpenAI
137
+ client = OpenAI()
138
+ response = client.chat.completions.create(
139
+ model=config.get("model", "gpt-4o"),
140
+ messages=[{"role": "user", "content": prompt}],
141
+ )
142
+ return response.choices[0].message.content
143
+ ```
144
+
145
+ ### 2. Run a Workflow
146
+
84
147
  ```python
85
148
  from soe import orchestrate, create_all_nodes
86
149
  from soe.local_backends import create_local_backends
@@ -98,8 +161,8 @@ example_workflow:
98
161
  # Create backends (storage for context, workflows, etc.)
99
162
  backends = create_local_backends("./data")
100
163
 
101
- # Create all node handlers
102
- nodes, broadcast = create_all_nodes(backends)
164
+ # Create all node handlers (pass your call_llm function)
165
+ nodes, broadcast = create_all_nodes(backends, call_llm=call_llm)
103
166
 
104
167
  # Run the workflow
105
168
  execution_id = orchestrate(
@@ -121,9 +184,9 @@ execution_id = orchestrate(
121
184
 
122
185
  | Audience | Start Here |
123
186
  |----------|------------|
124
- | **Builders** (workflow authors) | [Documentation](docs/index.md) — Step-by-step chapters |
125
- | **Engineers** (infrastructure) | [ARCHITECTURE.md](ai_docs/ARCHITECTURE.md) — Design philosophy |
126
- | **Researchers** (advanced patterns) | [Advanced Patterns](docs/advanced_patterns/index.md) — Swarm, hybrid, self-evolving |
187
+ | **Builders** (workflow authors) | [Documentation](soe/docs/index.md) — Step-by-step chapters |
188
+ | **Engineers** (infrastructure) | [Infrastructure Guide](soe/docs/guide_10_infrastructure.md) — Backend protocols |
189
+ | **Researchers** (advanced patterns) | [Advanced Patterns](docs/advanced_patterns/) — Swarm, hybrid, self-evolving |
127
190
 
128
191
  ---
129
192
 
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "soe-ai"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "Signal-driven Orchestration Engine - Agent orchestration with event-driven workflow engine"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
11
11
  license = "MIT"
12
12
  authors = [
13
- {name = "Pedro Garcia", email = "pedro@example.com"}
13
+ {name = "Pedro Garcia", email = "pgarcia14180@gmail.com"}
14
14
  ]
15
15
  keywords = ["orchestration", "agent", "workflow", "automation"]
16
16
  classifiers = [
@@ -49,11 +49,12 @@ Documentation = "https://github.com/pgarcia14180/soe/tree/master/docs"
49
49
  Repository = "https://github.com/pgarcia14180/soe"
50
50
  Issues = "https://github.com/pgarcia14180/soe/issues"
51
51
 
52
- [tool.setuptools]
53
- packages = ["soe"]
52
+ [tool.setuptools.packages.find]
53
+ include = ["soe*"]
54
+ exclude = ["tests*"]
54
55
 
55
56
  [tool.setuptools.package-data]
56
- soe = ["py.typed"]
57
+ soe = ["py.typed", "docs/**/*"]
57
58
 
58
59
  [tool.black]
59
60
  line-length = 88
@@ -1,7 +1,6 @@
1
1
  from uuid import uuid4
2
- from typing import Dict, List, Any, Union, Callable, Optional
3
- from .types import Backends, BroadcastSignalsCaller
4
- from .local_backends import EventTypes
2
+ from typing import Dict, List, Any, Union, Optional
3
+ from .types import Backends, BroadcastSignalsCaller, NodeCaller, EventTypes, WorkflowValidationError
5
4
  from .lib.register_event import register_event
6
5
  from .lib.yaml_parser import parse_yaml
7
6
  from .lib.operational import add_operational_state
@@ -131,7 +130,7 @@ def orchestrate(
131
130
  def broadcast_signals(
132
131
  id: str,
133
132
  signals: List[str],
134
- nodes: Dict[str, Callable[[str, Dict[str, Any]], None]],
133
+ nodes: Dict[str, NodeCaller],
135
134
  backends: Backends,
136
135
  ) -> None:
137
136
  """Broadcast signals to matching nodes in the current workflow"""
@@ -139,7 +138,7 @@ def broadcast_signals(
139
138
 
140
139
  register_event(backends, id, EventTypes.SIGNALS_BROADCAST, {"signals": signals})
141
140
 
142
- workflows_registry = backends.workflow.soe_get_workflows_registry(id)
141
+ workflows_registry = backends.workflow.get_workflows_registry(id)
143
142
 
144
143
  workflow_name = backends.workflow.get_current_workflow_name(id)
145
144
  workflow = workflows_registry.get(workflow_name, {})
@@ -0,0 +1,39 @@
1
+ """
2
+ Built-in tools registry
3
+ """
4
+
5
+ from .soe_inject_workflow import create_soe_inject_workflow_tool
6
+ from .soe_inject_node import create_soe_inject_node_tool
7
+ from .soe_get_workflows import create_soe_get_workflows_tool
8
+ from .soe_get_available_tools import create_soe_get_available_tools_tool
9
+ from .soe_explore_docs import create_soe_explore_docs_tool
10
+ from .soe_remove_workflow import create_soe_remove_workflow_tool
11
+ from .soe_remove_node import create_soe_remove_node_tool
12
+ from .soe_get_context import create_soe_get_context_tool
13
+ from .soe_update_context import create_soe_update_context_tool
14
+ from .soe_copy_context import create_soe_copy_context_tool
15
+ from .soe_list_contexts import create_soe_list_contexts_tool
16
+ from .soe_add_signal import create_soe_add_signal_tool
17
+ from .soe_call_tool import create_soe_call_tool_tool
18
+
19
+ # Registry of all available built-in tools
20
+ BUILTIN_TOOLS = {
21
+ "soe_inject_workflow": create_soe_inject_workflow_tool,
22
+ "soe_inject_node": create_soe_inject_node_tool,
23
+ "soe_get_workflows": create_soe_get_workflows_tool,
24
+ "soe_get_available_tools": create_soe_get_available_tools_tool,
25
+ "soe_explore_docs": create_soe_explore_docs_tool,
26
+ "soe_remove_workflow": create_soe_remove_workflow_tool,
27
+ "soe_remove_node": create_soe_remove_node_tool,
28
+ "soe_get_context": create_soe_get_context_tool,
29
+ "soe_update_context": create_soe_update_context_tool,
30
+ "soe_copy_context": create_soe_copy_context_tool,
31
+ "soe_list_contexts": create_soe_list_contexts_tool,
32
+ "soe_add_signal": create_soe_add_signal_tool,
33
+ "soe_call_tool": create_soe_call_tool_tool,
34
+ }
35
+
36
+
37
+ def get_builtin_tool_factory(tool_name: str):
38
+ """Get factory function for built-in tool"""
39
+ return BUILTIN_TOOLS.get(tool_name)
@@ -0,0 +1,82 @@
1
+ """Built-in tool to add a signal to a node's event emissions."""
2
+
3
+ from typing import Dict, Any, Callable
4
+ from ..types import EventTypes
5
+ from ..lib.register_event import register_event
6
+
7
+
8
+ def create_soe_add_signal_tool(
9
+ execution_id: str,
10
+ backends,
11
+ tools_registry: dict = None,
12
+ ) -> Callable:
13
+ """
14
+ Factory function to create add_signal tool.
15
+ """
16
+
17
+ def add_signal(
18
+ workflow_name: str, node_name: str, signal_name: str, condition: str
19
+ ) -> Dict[str, Any]:
20
+ """
21
+ Add a signal to a node's event_emissions list.
22
+
23
+ Args:
24
+ workflow_name: Name of the workflow
25
+ node_name: Name of the node
26
+ signal_name: Name of the signal to add
27
+ condition: Jinja condition for the signal
28
+
29
+ Returns:
30
+ Success confirmation
31
+ """
32
+ workflows_registry = backends.workflow.get_workflows_registry(execution_id)
33
+
34
+ if workflow_name not in workflows_registry:
35
+ raise ValueError(f"Workflow '{workflow_name}' not found")
36
+
37
+ workflow = workflows_registry[workflow_name]
38
+ if node_name not in workflow:
39
+ raise ValueError(f"Node '{node_name}' not found in workflow '{workflow_name}'")
40
+
41
+ node_config = workflow[node_name]
42
+
43
+ if "event_emissions" not in node_config:
44
+ node_config["event_emissions"] = []
45
+
46
+ # Check if signal already exists
47
+ for emission in node_config["event_emissions"]:
48
+ if emission.get("signal_name") == signal_name:
49
+ # Update existing
50
+ emission["condition"] = condition
51
+ backends.workflow.save_workflows_registry(execution_id, workflows_registry)
52
+ return {
53
+ "status": "updated",
54
+ "message": f"Updated signal '{signal_name}' in node '{node_name}'"
55
+ }
56
+
57
+ # Add new signal
58
+ node_config["event_emissions"].append({
59
+ "signal_name": signal_name,
60
+ "condition": condition
61
+ })
62
+
63
+ backends.workflow.save_workflows_registry(execution_id, workflows_registry)
64
+
65
+ register_event(
66
+ backends,
67
+ execution_id,
68
+ EventTypes.NODE_EXECUTION,
69
+ {
70
+ "tool": "add_signal",
71
+ "workflow_name": workflow_name,
72
+ "node_name": node_name,
73
+ "signal": signal_name
74
+ },
75
+ )
76
+
77
+ return {
78
+ "status": "added",
79
+ "message": f"Added signal '{signal_name}' to node '{node_name}'"
80
+ }
81
+
82
+ return add_signal
@@ -0,0 +1,111 @@
1
+ """
2
+ Built-in dynamic tool invocation.
3
+
4
+ Allows LLMs to call any registered tool by name at runtime.
5
+ This enables meta-level tool orchestration.
6
+ """
7
+
8
+ import json
9
+ from typing import Dict, Any, Callable
10
+ from ..types import EventTypes
11
+ from ..lib.register_event import register_event
12
+
13
+
14
+ def create_soe_call_tool_tool(
15
+ execution_id: str,
16
+ backends,
17
+ tools_registry: dict,
18
+ ) -> Callable:
19
+ """
20
+ Factory function to create call_tool with access to tools registry.
21
+
22
+ Args:
23
+ execution_id: Current execution ID
24
+ backends: Backend services
25
+ tools_registry: Registry of available tools {name: {"function": callable, ...}}
26
+
27
+ Returns:
28
+ Configured call_tool function
29
+ """
30
+
31
+ def call_tool(tool_name: str, arguments: str = "{}") -> Dict[str, Any]:
32
+ """
33
+ Dynamically invoke a registered tool by name.
34
+
35
+ This is a meta-tool that allows calling any other tool at runtime.
36
+ Useful for dynamic workflows where the tool to call is determined
37
+ by context or user input.
38
+
39
+ Args:
40
+ tool_name: Name of the tool to invoke (must be registered)
41
+ arguments: JSON string of arguments to pass to the tool
42
+
43
+ Returns:
44
+ The result from the invoked tool, or an error dict
45
+
46
+ Example:
47
+ call_tool("get_secret", '{"key": "password"}')
48
+ call_tool("write_file", '{"path": "test.txt", "content": "hello"}')
49
+ """
50
+ # Parse arguments
51
+ try:
52
+ args = json.loads(arguments) if arguments else {}
53
+ except json.JSONDecodeError as e:
54
+ return {
55
+ "error": f"Invalid JSON arguments: {e}",
56
+ "tool_name": tool_name,
57
+ }
58
+
59
+ # Check if tool exists
60
+ if tool_name not in tools_registry:
61
+ available = list(tools_registry.keys())
62
+ return {
63
+ "error": f"Tool '{tool_name}' not found",
64
+ "available_tools": available[:20], # Limit to avoid huge responses
65
+ }
66
+
67
+ # Get tool function
68
+ tool_entry = tools_registry[tool_name]
69
+ if isinstance(tool_entry, dict):
70
+ tool_func = tool_entry.get("function")
71
+ elif callable(tool_entry):
72
+ tool_func = tool_entry
73
+ else:
74
+ return {"error": f"Invalid tool registry entry for '{tool_name}'"}
75
+
76
+ if not callable(tool_func):
77
+ return {"error": f"Tool '{tool_name}' is not callable"}
78
+
79
+ # Log the dynamic invocation
80
+ register_event(
81
+ backends,
82
+ execution_id,
83
+ EventTypes.TOOL_CALL,
84
+ {
85
+ "meta_tool": "call_tool",
86
+ "invoked_tool": tool_name,
87
+ "arguments": args,
88
+ },
89
+ )
90
+
91
+ # Invoke the tool
92
+ try:
93
+ result = tool_func(**args)
94
+ return {
95
+ "success": True,
96
+ "tool_name": tool_name,
97
+ "result": result,
98
+ }
99
+ except TypeError as e:
100
+ # Argument mismatch
101
+ return {
102
+ "error": f"Argument error for '{tool_name}': {e}",
103
+ "tool_name": tool_name,
104
+ }
105
+ except Exception as e:
106
+ return {
107
+ "error": f"Tool '{tool_name}' failed: {e}",
108
+ "tool_name": tool_name,
109
+ }
110
+
111
+ return call_tool
@@ -0,0 +1,80 @@
1
+ """
2
+ Built-in tool: copy_context
3
+ Allows agents to copy context fields between executions or within the same execution.
4
+ """
5
+
6
+ from typing import Dict, Any, Optional
7
+
8
+
9
+ def create_soe_copy_context_tool(backends, execution_id: str, tools_registry=None):
10
+ """
11
+ Factory that creates a copy_context tool bound to the current execution.
12
+
13
+ Args:
14
+ backends: Backend instances (needs context backend)
15
+ execution_id: Current execution ID
16
+ tools_registry: Tool registry (unused, for interface compatibility)
17
+
18
+ Returns:
19
+ Configured tool function
20
+ """
21
+
22
+ def copy_context(
23
+ source_execution_id: Optional[str] = None,
24
+ fields: Optional[Dict[str, str]] = None,
25
+ all_fields: bool = False,
26
+ target_execution_id: Optional[str] = None,
27
+ ) -> Dict[str, Any]:
28
+ """
29
+ Copy context fields between executions or within the same execution.
30
+
31
+ Args:
32
+ source_execution_id: Execution ID to copy from (default: current)
33
+ fields: Dict of {source_field: target_field} to copy
34
+ all_fields: If True, copy all non-operational fields
35
+ target_execution_id: Execution ID to copy to (default: current)
36
+
37
+ Returns:
38
+ Dict with copy results
39
+ """
40
+ source_id = source_execution_id or execution_id
41
+ target_id = target_execution_id or execution_id
42
+
43
+ # Get source context
44
+ source_context = backends.context.get_context(source_id)
45
+
46
+ # Filter out operational fields
47
+ source_filtered = {k: v for k, v in source_context.items() if not k.startswith("__")}
48
+
49
+ # Get target context
50
+ target_context = backends.context.get_context(target_id)
51
+
52
+ copied_fields = {}
53
+
54
+ if all_fields:
55
+ # Copy all non-operational fields
56
+ for field, value in source_filtered.items():
57
+ target_context[field] = value
58
+ copied_fields[field] = field
59
+ elif fields:
60
+ # Copy specific field mappings
61
+ for source_field, target_field in fields.items():
62
+ if source_field in source_filtered:
63
+ target_context[target_field] = source_filtered[source_field]
64
+ copied_fields[source_field] = target_field
65
+ else:
66
+ return {"error": f"Source field '{source_field}' not found in execution {source_id}"}
67
+ else:
68
+ return {"error": "Must specify either 'fields' mapping or 'all_fields=True'"}
69
+
70
+ # Save target context
71
+ backends.context.save_context(target_id, target_context)
72
+
73
+ return {
74
+ "status": "copied",
75
+ "source_execution": source_id,
76
+ "target_execution": target_id,
77
+ "fields_copied": copied_fields,
78
+ }
79
+
80
+ return copy_context