mantis-engine 0.6.0__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 (110) hide show
  1. mantis_engine-0.6.0/.github/workflows/publish.yml +27 -0
  2. mantis_engine-0.6.0/.gitignore +13 -0
  3. mantis_engine-0.6.0/LICENSE +21 -0
  4. mantis_engine-0.6.0/PKG-INFO +271 -0
  5. mantis_engine-0.6.0/README.md +235 -0
  6. mantis_engine-0.6.0/examples/demo.py +241 -0
  7. mantis_engine-0.6.0/examples/demo_langchain.py +384 -0
  8. mantis_engine-0.6.0/examples/demo_live.py +386 -0
  9. mantis_engine-0.6.0/examples/demo_runtime.py +400 -0
  10. mantis_engine-0.6.0/examples/demo_runtime_full.py +265 -0
  11. mantis_engine-0.6.0/mantis/__init__.py +75 -0
  12. mantis_engine-0.6.0/mantis/__main__.py +59 -0
  13. mantis_engine-0.6.0/mantis/adapters/__init__.py +10 -0
  14. mantis_engine-0.6.0/mantis/adapters/canvas_adapter.py +293 -0
  15. mantis_engine-0.6.0/mantis/adapters/langchain_adapter.py +143 -0
  16. mantis_engine-0.6.0/mantis/adapters/sse_adapter.py +163 -0
  17. mantis_engine-0.6.0/mantis/adapters/xgen_adapter.py +129 -0
  18. mantis_engine-0.6.0/mantis/context/__init__.py +0 -0
  19. mantis_engine-0.6.0/mantis/context/conversation.py +95 -0
  20. mantis_engine-0.6.0/mantis/exceptions.py +43 -0
  21. mantis_engine-0.6.0/mantis/executor/__init__.py +15 -0
  22. mantis_engine-0.6.0/mantis/executor/flow.py +128 -0
  23. mantis_engine-0.6.0/mantis/executor/phases/__init__.py +16 -0
  24. mantis_engine-0.6.0/mantis/executor/phases/act.py +116 -0
  25. mantis_engine-0.6.0/mantis/executor/phases/observe.py +33 -0
  26. mantis_engine-0.6.0/mantis/executor/phases/prepare.py +40 -0
  27. mantis_engine-0.6.0/mantis/executor/phases/protocol.py +26 -0
  28. mantis_engine-0.6.0/mantis/executor/phases/resolve.py +58 -0
  29. mantis_engine-0.6.0/mantis/executor/pipeline.py +579 -0
  30. mantis_engine-0.6.0/mantis/executor/protocol.py +74 -0
  31. mantis_engine-0.6.0/mantis/generate/__init__.py +8 -0
  32. mantis_engine-0.6.0/mantis/generate/tool_generator.py +347 -0
  33. mantis_engine-0.6.0/mantis/llm/__init__.py +13 -0
  34. mantis_engine-0.6.0/mantis/llm/openai_provider.py +132 -0
  35. mantis_engine-0.6.0/mantis/llm/protocol.py +43 -0
  36. mantis_engine-0.6.0/mantis/middleware/__init__.py +14 -0
  37. mantis_engine-0.6.0/mantis/middleware/approval.py +83 -0
  38. mantis_engine-0.6.0/mantis/middleware/base.py +87 -0
  39. mantis_engine-0.6.0/mantis/middleware/graph_search.py +168 -0
  40. mantis_engine-0.6.0/mantis/middleware/state.py +125 -0
  41. mantis_engine-0.6.0/mantis/middleware/trace.py +78 -0
  42. mantis_engine-0.6.0/mantis/models/__init__.py +1 -0
  43. mantis_engine-0.6.0/mantis/models/node.py +38 -0
  44. mantis_engine-0.6.0/mantis/providers/__init__.py +30 -0
  45. mantis_engine-0.6.0/mantis/runtime/__init__.py +5 -0
  46. mantis_engine-0.6.0/mantis/runtime/context.py +44 -0
  47. mantis_engine-0.6.0/mantis/runtime/hooks/__init__.py +1 -0
  48. mantis_engine-0.6.0/mantis/runtime/hooks/protocol.py +12 -0
  49. mantis_engine-0.6.0/mantis/runtime/hooks/trace.py +27 -0
  50. mantis_engine-0.6.0/mantis/runtime/parse.py +121 -0
  51. mantis_engine-0.6.0/mantis/runtime/plan.py +60 -0
  52. mantis_engine-0.6.0/mantis/runtime/port_store.py +87 -0
  53. mantis_engine-0.6.0/mantis/runtime/route_resolver.py +131 -0
  54. mantis_engine-0.6.0/mantis/runtime/runners/__init__.py +1 -0
  55. mantis_engine-0.6.0/mantis/runtime/runners/agent.py +137 -0
  56. mantis_engine-0.6.0/mantis/runtime/runners/bypass.py +29 -0
  57. mantis_engine-0.6.0/mantis/runtime/runners/default.py +28 -0
  58. mantis_engine-0.6.0/mantis/runtime/runners/io.py +35 -0
  59. mantis_engine-0.6.0/mantis/runtime/runners/protocol.py +10 -0
  60. mantis_engine-0.6.0/mantis/runtime/runners/provider.py +29 -0
  61. mantis_engine-0.6.0/mantis/runtime/runners/router.py +38 -0
  62. mantis_engine-0.6.0/mantis/runtime/runtime.py +283 -0
  63. mantis_engine-0.6.0/mantis/runtime/stream_manager.py +62 -0
  64. mantis_engine-0.6.0/mantis/safety/__init__.py +0 -0
  65. mantis_engine-0.6.0/mantis/safety/approval.py +151 -0
  66. mantis_engine-0.6.0/mantis/sandbox/__init__.py +11 -0
  67. mantis_engine-0.6.0/mantis/sandbox/runner.py +89 -0
  68. mantis_engine-0.6.0/mantis/sandbox/sandbox.py +204 -0
  69. mantis_engine-0.6.0/mantis/sandbox/tools.py +98 -0
  70. mantis_engine-0.6.0/mantis/search/__init__.py +18 -0
  71. mantis_engine-0.6.0/mantis/search/graph_search.py +820 -0
  72. mantis_engine-0.6.0/mantis/state/__init__.py +5 -0
  73. mantis_engine-0.6.0/mantis/state/store.py +113 -0
  74. mantis_engine-0.6.0/mantis/state/tools.py +87 -0
  75. mantis_engine-0.6.0/mantis/testing/__init__.py +10 -0
  76. mantis_engine-0.6.0/mantis/testing/dummy_args.py +41 -0
  77. mantis_engine-0.6.0/mantis/testing/pytest_runner.py +72 -0
  78. mantis_engine-0.6.0/mantis/testing/tool_tester.py +155 -0
  79. mantis_engine-0.6.0/mantis/tools/__init__.py +12 -0
  80. mantis_engine-0.6.0/mantis/tools/bridge.py +57 -0
  81. mantis_engine-0.6.0/mantis/tools/builtins.py +43 -0
  82. mantis_engine-0.6.0/mantis/tools/decorator.py +96 -0
  83. mantis_engine-0.6.0/mantis/tools/meta.py +124 -0
  84. mantis_engine-0.6.0/mantis/tools/registry.py +199 -0
  85. mantis_engine-0.6.0/mantis/trace/__init__.py +0 -0
  86. mantis_engine-0.6.0/mantis/trace/collector.py +107 -0
  87. mantis_engine-0.6.0/mantis/trace/exporter.py +27 -0
  88. mantis_engine-0.6.0/mantis/workflow/__init__.py +20 -0
  89. mantis_engine-0.6.0/mantis/workflow/generator.py +225 -0
  90. mantis_engine-0.6.0/mantis/workflow/models.py +79 -0
  91. mantis_engine-0.6.0/mantis/workflow/runner.py +259 -0
  92. mantis_engine-0.6.0/mantis/workflow/store.py +37 -0
  93. mantis_engine-0.6.0/mantis/workflow/tools.py +266 -0
  94. mantis_engine-0.6.0/pyproject.toml +55 -0
  95. mantis_engine-0.6.0/tests/test_agent.py +368 -0
  96. mantis_engine-0.6.0/tests/test_approval.py +63 -0
  97. mantis_engine-0.6.0/tests/test_context.py +74 -0
  98. mantis_engine-0.6.0/tests/test_engine_integration.py +521 -0
  99. mantis_engine-0.6.0/tests/test_generator.py +113 -0
  100. mantis_engine-0.6.0/tests/test_graph_tool.py +447 -0
  101. mantis_engine-0.6.0/tests/test_model_client.py +121 -0
  102. mantis_engine-0.6.0/tests/test_phases.py +186 -0
  103. mantis_engine-0.6.0/tests/test_registry_v2.py +175 -0
  104. mantis_engine-0.6.0/tests/test_runtime.py +382 -0
  105. mantis_engine-0.6.0/tests/test_sandbox.py +34 -0
  106. mantis_engine-0.6.0/tests/test_sandbox_tools.py +54 -0
  107. mantis_engine-0.6.0/tests/test_tools.py +68 -0
  108. mantis_engine-0.6.0/tests/test_trace.py +68 -0
  109. mantis_engine-0.6.0/tests/test_v3_integration.py +497 -0
  110. mantis_engine-0.6.0/tests/test_v6_integration.py +420 -0
@@ -0,0 +1,27 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ id-token: write
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.11"
20
+
21
+ - name: Build
22
+ run: |
23
+ pip install build
24
+ python -m build
25
+
26
+ - name: Publish to PyPI
27
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .pytest_cache/
7
+ .eggs/
8
+ *.egg
9
+ .env
10
+ .venv/
11
+ .claude/
12
+ feedback-*.md
13
+ blueprint-*.md
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Jinsoo Kim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,271 @@
1
+ Metadata-Version: 2.4
2
+ Name: mantis-engine
3
+ Version: 0.6.0
4
+ Summary: AI Agent execution engine — plug a model, get a running agent.
5
+ Project-URL: Homepage, https://github.com/PlateerLab/mantis
6
+ Project-URL: Repository, https://github.com/PlateerLab/mantis
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Keywords: agent,ai,execution-engine,llm,workflow
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Libraries
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: httpx>=0.27
20
+ Provides-Extra: all
21
+ Requires-Dist: asyncpg>=0.29; extra == 'all'
22
+ Requires-Dist: docker>=7.0; extra == 'all'
23
+ Requires-Dist: graph-tool-call>=0.15; extra == 'all'
24
+ Provides-Extra: dev
25
+ Requires-Dist: build>=1.0; extra == 'dev'
26
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0; extra == 'dev'
28
+ Requires-Dist: twine>=5.0; extra == 'dev'
29
+ Provides-Extra: sandbox
30
+ Requires-Dist: docker>=7.0; extra == 'sandbox'
31
+ Provides-Extra: search
32
+ Requires-Dist: graph-tool-call>=0.15; extra == 'search'
33
+ Provides-Extra: state
34
+ Requires-Dist: asyncpg>=0.29; extra == 'state'
35
+ Description-Content-Type: text/markdown
36
+
37
+ <h1 align="center">Mantis</h1>
38
+
39
+ <p align="center">
40
+ <a href="https://pypi.org/project/mantis-engine/"><img src="https://img.shields.io/pypi/pyversions/mantis-engine" alt="Python"></a>
41
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
42
+ </p>
43
+
44
+ <p><strong>Workflow execution engine.</strong></p>
45
+
46
+ Feed a workflow graph (nodes + edges), get logical execution. Replaces tangled, spaghetti executors with clean 4-phase pipeline: **PARSE → PLAN → EXECUTE → FINALIZE**.
47
+
48
+ ```python
49
+ from mantis import WorkflowRuntime
50
+
51
+ runtime = WorkflowRuntime(model=my_llm, tools=[calculate, get_weather])
52
+
53
+ # Execute a workflow graph
54
+ async for event in runtime.execute(workflow_data, input_data):
55
+ print(event)
56
+
57
+ # Or collect the final result
58
+ result = await runtime.execute_collect(workflow_data, input_data)
59
+ ```
60
+
61
+ ## Installation
62
+
63
+ ```bash
64
+ pip install mantis-engine # core only (httpx)
65
+ pip install mantis-engine[search] # + graph-tool-call (>=0.15)
66
+ pip install mantis-engine[sandbox] # + Docker sandbox
67
+ pip install mantis-engine[state] # + PostgreSQL checkpointing
68
+ pip install mantis-engine[all] # everything
69
+ ```
70
+
71
+ ## How It Works
72
+
73
+ ```
74
+ ┌─ Caller (xgen, FastAPI, script) ────────────────────────────────────┐
75
+ │ │
76
+ │ workflow_data ─── {nodes: [...], edges: [...]} │
77
+ │ input_data ────── data for startnode │
78
+ │ model ─────────── LLMProvider (for agent nodes) │
79
+ │ tools ─────────── [@tool functions] (for agent nodes) │
80
+ │ hooks ─────────── [TraceHook, ApprovalHook, ...] │
81
+ │ providers ─────── search / sandbox / workflows / state │
82
+ │ │
83
+ └──────────────────────────┬───────────────────────────────────────────┘
84
+ │ runtime.execute(workflow_data, input_data)
85
+
86
+ ╔═══════════════════════════════════════════════════════════════════════════╗
87
+ ║ WorkflowRuntime ║
88
+ ╠═══════════════════════════════════════════════════════════════════════════╣
89
+ ║ ║
90
+ ║ ┌─ PARSE ──────────────────────────────────────────────────────────┐ ║
91
+ ║ │ JSON → NodeInfo + EdgeInfo │ ║
92
+ ║ │ disabled/disconnected removal · port dependency · runner mapping │ ║
93
+ ║ └──────────────────────────┬───────────────────────────────────────┘ ║
94
+ ║ ▼ ║
95
+ ║ ┌─ PLAN ───────────────────────────────────────────────────────────┐ ║
96
+ ║ │ Kahn's algorithm topological sort · cycle detection │ ║
97
+ ║ └──────────────────────────┬───────────────────────────────────────┘ ║
98
+ ║ ▼ ║
99
+ ║ ┌─ EXECUTE ────────────────────────────────────────────────────────┐ ║
100
+ ║ │ │ ║
101
+ ║ │ for node in execution_order: │ ║
102
+ ║ │ excluded? → skip │ ║
103
+ ║ │ PortStore.wire() → inputs │ ║
104
+ ║ │ Hook.before_node() │ ║
105
+ ║ │ NodeRunner.run() → result ┌─────────────────────┐ │ ║
106
+ ║ │ Hook.after_node() │ NodeRunner 6 types │ │ ║
107
+ ║ │ PortStore.store() │ Default (compute) │ │ ║
108
+ ║ │ RouteResolver.evaluate() │ Agent (LLM) │ │ ║
109
+ ║ │ │ Router (N-way) │ │ ║
110
+ ║ │ yield event │ Provider (config) │ │ ║
111
+ ║ │ │ IO (in/out) │ │ ║
112
+ ║ │ │ Bypass (pass) │ │ ║
113
+ ║ │ └─────────────────────┘ │ ║
114
+ ║ └──────────────────────────┬───────────────────────────────────────┘ ║
115
+ ║ ▼ ║
116
+ ║ ┌─ FINALIZE ───────────────────────────────────────────────────────┐ ║
117
+ ║ │ collect endnode results · cleanup streams · Hook.on_complete() │ ║
118
+ ║ └──────────────────────────────────────────────────────────────────┘ ║
119
+ ║ ║
120
+ ║ ┌─ Internal Modules ───────────────────────────────────────────────┐ ║
121
+ ║ │ PortStore ────── port-based data wiring between nodes │ ║
122
+ ║ │ StreamManager ── generator fan-out · BufferedFactory · replay │ ║
123
+ ║ │ RouteResolver ── router branching · subgraph DFS exclusion │ ║
124
+ ║ │ ToolRegistry ─── @tool · file · dir · runtime creation · bridge │ ║
125
+ ║ │ ToolGenerator ── LLM code gen → Docker verify → register │ ║
126
+ ║ │ ToolTester ──── schema → smoke → pytest 3-level verification │ ║
127
+ ║ │ WorkflowGenerator ── LLM → WorkflowDef auto-design │ ║
128
+ ║ └──────────────────────────────────────────────────────────────────┘ ║
129
+ ║ ║
130
+ ╠═══════════════════════════════════════════════════════════════════════════╣
131
+ ║ HOOKS (applied per node) ║
132
+ ║ before_node → [execute] → after_node → on_complete ║
133
+ ║ Approval · Trace · custom ║
134
+ ╠═══════════════════════════════════════════════════════════════════════════╣
135
+ ║ ADAPTERS ║
136
+ ║ event_to_sse · langchain_adapter · xgen_adapter · bridge ║
137
+ ╚═══════════════════════════════════════════════════════════════════════════╝
138
+
139
+
140
+ Event Stream / Result
141
+ ```
142
+
143
+ The main loop has **zero if/elif for node types**. All node-specific logic dispatches to NodeRunner handlers.
144
+
145
+ ## Node Runners
146
+
147
+ | Runner | Handles | What it does |
148
+ |--------|---------|-------------|
149
+ | DefaultRunner | compute, transform | `node.execute(**inputs)` → store result |
150
+ | AgentRunner | LLM agents | PipelineExecutor loop with ToolRegistry, search, sandbox. Streams tokens + tool calls. |
151
+ | RouterRunner | conditional branch | Extract routing key → RouteResolver excludes unselected subgraphs via DFS |
152
+ | ProviderRunner | model, MCP, config | Create config object → downstream nodes receive via port wiring |
153
+ | IORunner | start, end | Start: inject input_data. End: collect results, stream generators. |
154
+ | BypassRunner | bypass=True | Pass through input without execution |
155
+
156
+ ## Agent Nodes
157
+
158
+ When an agent node has no `node_class`, AgentRunner creates a `PipelineExecutor` with all connected providers:
159
+
160
+ ```
161
+ AgentRunner._run_mantis_agent()
162
+ → PipelineExecutor(model, ToolRegistry, search, sandbox, workflows, state)
163
+ → RESOLVE: LLM call (with tool search filtering)
164
+ → ACT: tool execution (with create_tool + Docker verify if sandbox)
165
+ → OBSERVE: checkpoint
166
+ → loop until done
167
+ → intermediate events (tool_call, tool_result) propagate to workflow stream
168
+ ```
169
+
170
+ ## Tool Creation & Verification
171
+
172
+ When sandbox is provided, agent nodes can create tools at runtime:
173
+
174
+ ```
175
+ create_tool → LLM generates @tool code
176
+ → Docker sandbox: syntax check
177
+ → ToolTester: smoke test + pytest
178
+ → pass → ToolRegistry registration → available next iteration
179
+ ```
180
+
181
+ 3-level verification:
182
+
183
+ | Level | Method | Sandbox |
184
+ |-------|--------|---------|
185
+ | 1 | `validate_schema()` | No |
186
+ | 2 | `smoke_test()` | Optional |
187
+ | 3 | `run_pytest()` | Required |
188
+
189
+ ## Tool Search (graph-tool-call)
190
+
191
+ ```python
192
+ from mantis.search import GraphToolManager, GraphToolConfig
193
+
194
+ manager = GraphToolManager(GraphToolConfig(search_mode="enhanced"))
195
+
196
+ tools = await manager.aretrieve("cancel order", top_k=5)
197
+ plan = manager.plan_workflow("Process refund for order #123")
198
+ compressed = manager.compress_result(huge_response, max_chars=4000)
199
+ ```
200
+
201
+ ## PipelineExecutor (Agent Mode)
202
+
203
+ For standalone agent execution without a workflow graph:
204
+
205
+ ```python
206
+ from mantis import PipelineExecutor, tool
207
+ from mantis.providers import ModelClient
208
+
209
+ @tool(name="calc", description="Calculate", parameters={"expr": {"type": "string"}})
210
+ async def calc(expr: str) -> dict:
211
+ return {"result": eval(expr)}
212
+
213
+ executor = PipelineExecutor(
214
+ model=ModelClient(model="gpt-4o-mini", api_key="sk-..."),
215
+ tools=[calc],
216
+ )
217
+ result = await executor.run("What is 42 * 17?")
218
+ ```
219
+
220
+ ## Events
221
+
222
+ ```python
223
+ async for event in runtime.execute(workflow_data, input_data):
224
+ match event["type"]:
225
+ case "workflow_start": ... # node count, edge count
226
+ case "execution_plan": ... # order, node_count
227
+ case "node_start": ... # node_id, function_id, name, input_keys, timestamp
228
+ case "node_complete": ... # node_id, result_keys, output_data, duration_ms
229
+ case "node_skip": ... # node_id, reason (excluded/bypass)
230
+ case "route_decision": ... # selected_port
231
+ case "stream_chunk": ... # node_id, chunk
232
+ case "agent_tool_call": ... # node_id, tool_name, args
233
+ case "agent_tool_result":... # node_id, tool_name, result
234
+ case "workflow_complete": ... # final results
235
+ case "workflow_error": ... # error detail
236
+ ```
237
+
238
+ ## Package Structure
239
+
240
+ ```
241
+ mantis/
242
+ ├── runtime/ # WorkflowRuntime (core engine)
243
+ │ ├── runtime.py # entry point: PARSE → PLAN → EXECUTE → FINALIZE
244
+ │ ├── parse.py # workflow JSON → NodeInfo/EdgeInfo
245
+ │ ├── plan.py # topological sort (Kahn's algorithm)
246
+ │ ├── execute.py # main loop + event emission
247
+ │ ├── port_store.py # port-based data wiring
248
+ │ ├── stream_manager.py # generator fan-out / BufferedFactory
249
+ │ ├── route_resolver.py # router branching + DFS exclusion
250
+ │ ├── runners/ # NodeRunner handlers (6 types)
251
+ │ └── hooks/ # ExecutionHook (trace, approval)
252
+
253
+ ├── executor/ # PipelineExecutor (agent mode)
254
+ │ ├── pipeline.py # PREPARE → RESOLVE → ACT → OBSERVE loop
255
+ │ ├── flow.py # FlowState for deterministic flows
256
+ │ └── phases/ # pluggable phase implementations
257
+
258
+ ├── tools/ # @tool decorator, ToolRegistry, bridge
259
+ ├── search/ # GraphToolManager (graph-tool-call)
260
+ ├── sandbox/ # DockerSandbox
261
+ ├── generate/ # ToolGenerator (LLM → code → verify → register)
262
+ ├── testing/ # ToolTester (schema → smoke → pytest)
263
+ ├── workflow/ # WorkflowDef, Store, Generator
264
+ ├── adapters/ # SSE, xgen, LangChain adapters
265
+ ├── models/ # NodeInfo, PortInfo, EdgeInfo
266
+ └── exceptions.py # MantisError hierarchy
267
+ ```
268
+
269
+ ## License
270
+
271
+ MIT
@@ -0,0 +1,235 @@
1
+ <h1 align="center">Mantis</h1>
2
+
3
+ <p align="center">
4
+ <a href="https://pypi.org/project/mantis-engine/"><img src="https://img.shields.io/pypi/pyversions/mantis-engine" alt="Python"></a>
5
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
6
+ </p>
7
+
8
+ <p><strong>Workflow execution engine.</strong></p>
9
+
10
+ Feed a workflow graph (nodes + edges), get logical execution. Replaces tangled, spaghetti executors with clean 4-phase pipeline: **PARSE → PLAN → EXECUTE → FINALIZE**.
11
+
12
+ ```python
13
+ from mantis import WorkflowRuntime
14
+
15
+ runtime = WorkflowRuntime(model=my_llm, tools=[calculate, get_weather])
16
+
17
+ # Execute a workflow graph
18
+ async for event in runtime.execute(workflow_data, input_data):
19
+ print(event)
20
+
21
+ # Or collect the final result
22
+ result = await runtime.execute_collect(workflow_data, input_data)
23
+ ```
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install mantis-engine # core only (httpx)
29
+ pip install mantis-engine[search] # + graph-tool-call (>=0.15)
30
+ pip install mantis-engine[sandbox] # + Docker sandbox
31
+ pip install mantis-engine[state] # + PostgreSQL checkpointing
32
+ pip install mantis-engine[all] # everything
33
+ ```
34
+
35
+ ## How It Works
36
+
37
+ ```
38
+ ┌─ Caller (xgen, FastAPI, script) ────────────────────────────────────┐
39
+ │ │
40
+ │ workflow_data ─── {nodes: [...], edges: [...]} │
41
+ │ input_data ────── data for startnode │
42
+ │ model ─────────── LLMProvider (for agent nodes) │
43
+ │ tools ─────────── [@tool functions] (for agent nodes) │
44
+ │ hooks ─────────── [TraceHook, ApprovalHook, ...] │
45
+ │ providers ─────── search / sandbox / workflows / state │
46
+ │ │
47
+ └──────────────────────────┬───────────────────────────────────────────┘
48
+ │ runtime.execute(workflow_data, input_data)
49
+
50
+ ╔═══════════════════════════════════════════════════════════════════════════╗
51
+ ║ WorkflowRuntime ║
52
+ ╠═══════════════════════════════════════════════════════════════════════════╣
53
+ ║ ║
54
+ ║ ┌─ PARSE ──────────────────────────────────────────────────────────┐ ║
55
+ ║ │ JSON → NodeInfo + EdgeInfo │ ║
56
+ ║ │ disabled/disconnected removal · port dependency · runner mapping │ ║
57
+ ║ └──────────────────────────┬───────────────────────────────────────┘ ║
58
+ ║ ▼ ║
59
+ ║ ┌─ PLAN ───────────────────────────────────────────────────────────┐ ║
60
+ ║ │ Kahn's algorithm topological sort · cycle detection │ ║
61
+ ║ └──────────────────────────┬───────────────────────────────────────┘ ║
62
+ ║ ▼ ║
63
+ ║ ┌─ EXECUTE ────────────────────────────────────────────────────────┐ ║
64
+ ║ │ │ ║
65
+ ║ │ for node in execution_order: │ ║
66
+ ║ │ excluded? → skip │ ║
67
+ ║ │ PortStore.wire() → inputs │ ║
68
+ ║ │ Hook.before_node() │ ║
69
+ ║ │ NodeRunner.run() → result ┌─────────────────────┐ │ ║
70
+ ║ │ Hook.after_node() │ NodeRunner 6 types │ │ ║
71
+ ║ │ PortStore.store() │ Default (compute) │ │ ║
72
+ ║ │ RouteResolver.evaluate() │ Agent (LLM) │ │ ║
73
+ ║ │ │ Router (N-way) │ │ ║
74
+ ║ │ yield event │ Provider (config) │ │ ║
75
+ ║ │ │ IO (in/out) │ │ ║
76
+ ║ │ │ Bypass (pass) │ │ ║
77
+ ║ │ └─────────────────────┘ │ ║
78
+ ║ └──────────────────────────┬───────────────────────────────────────┘ ║
79
+ ║ ▼ ║
80
+ ║ ┌─ FINALIZE ───────────────────────────────────────────────────────┐ ║
81
+ ║ │ collect endnode results · cleanup streams · Hook.on_complete() │ ║
82
+ ║ └──────────────────────────────────────────────────────────────────┘ ║
83
+ ║ ║
84
+ ║ ┌─ Internal Modules ───────────────────────────────────────────────┐ ║
85
+ ║ │ PortStore ────── port-based data wiring between nodes │ ║
86
+ ║ │ StreamManager ── generator fan-out · BufferedFactory · replay │ ║
87
+ ║ │ RouteResolver ── router branching · subgraph DFS exclusion │ ║
88
+ ║ │ ToolRegistry ─── @tool · file · dir · runtime creation · bridge │ ║
89
+ ║ │ ToolGenerator ── LLM code gen → Docker verify → register │ ║
90
+ ║ │ ToolTester ──── schema → smoke → pytest 3-level verification │ ║
91
+ ║ │ WorkflowGenerator ── LLM → WorkflowDef auto-design │ ║
92
+ ║ └──────────────────────────────────────────────────────────────────┘ ║
93
+ ║ ║
94
+ ╠═══════════════════════════════════════════════════════════════════════════╣
95
+ ║ HOOKS (applied per node) ║
96
+ ║ before_node → [execute] → after_node → on_complete ║
97
+ ║ Approval · Trace · custom ║
98
+ ╠═══════════════════════════════════════════════════════════════════════════╣
99
+ ║ ADAPTERS ║
100
+ ║ event_to_sse · langchain_adapter · xgen_adapter · bridge ║
101
+ ╚═══════════════════════════════════════════════════════════════════════════╝
102
+
103
+
104
+ Event Stream / Result
105
+ ```
106
+
107
+ The main loop has **zero if/elif for node types**. All node-specific logic dispatches to NodeRunner handlers.
108
+
109
+ ## Node Runners
110
+
111
+ | Runner | Handles | What it does |
112
+ |--------|---------|-------------|
113
+ | DefaultRunner | compute, transform | `node.execute(**inputs)` → store result |
114
+ | AgentRunner | LLM agents | PipelineExecutor loop with ToolRegistry, search, sandbox. Streams tokens + tool calls. |
115
+ | RouterRunner | conditional branch | Extract routing key → RouteResolver excludes unselected subgraphs via DFS |
116
+ | ProviderRunner | model, MCP, config | Create config object → downstream nodes receive via port wiring |
117
+ | IORunner | start, end | Start: inject input_data. End: collect results, stream generators. |
118
+ | BypassRunner | bypass=True | Pass through input without execution |
119
+
120
+ ## Agent Nodes
121
+
122
+ When an agent node has no `node_class`, AgentRunner creates a `PipelineExecutor` with all connected providers:
123
+
124
+ ```
125
+ AgentRunner._run_mantis_agent()
126
+ → PipelineExecutor(model, ToolRegistry, search, sandbox, workflows, state)
127
+ → RESOLVE: LLM call (with tool search filtering)
128
+ → ACT: tool execution (with create_tool + Docker verify if sandbox)
129
+ → OBSERVE: checkpoint
130
+ → loop until done
131
+ → intermediate events (tool_call, tool_result) propagate to workflow stream
132
+ ```
133
+
134
+ ## Tool Creation & Verification
135
+
136
+ When sandbox is provided, agent nodes can create tools at runtime:
137
+
138
+ ```
139
+ create_tool → LLM generates @tool code
140
+ → Docker sandbox: syntax check
141
+ → ToolTester: smoke test + pytest
142
+ → pass → ToolRegistry registration → available next iteration
143
+ ```
144
+
145
+ 3-level verification:
146
+
147
+ | Level | Method | Sandbox |
148
+ |-------|--------|---------|
149
+ | 1 | `validate_schema()` | No |
150
+ | 2 | `smoke_test()` | Optional |
151
+ | 3 | `run_pytest()` | Required |
152
+
153
+ ## Tool Search (graph-tool-call)
154
+
155
+ ```python
156
+ from mantis.search import GraphToolManager, GraphToolConfig
157
+
158
+ manager = GraphToolManager(GraphToolConfig(search_mode="enhanced"))
159
+
160
+ tools = await manager.aretrieve("cancel order", top_k=5)
161
+ plan = manager.plan_workflow("Process refund for order #123")
162
+ compressed = manager.compress_result(huge_response, max_chars=4000)
163
+ ```
164
+
165
+ ## PipelineExecutor (Agent Mode)
166
+
167
+ For standalone agent execution without a workflow graph:
168
+
169
+ ```python
170
+ from mantis import PipelineExecutor, tool
171
+ from mantis.providers import ModelClient
172
+
173
+ @tool(name="calc", description="Calculate", parameters={"expr": {"type": "string"}})
174
+ async def calc(expr: str) -> dict:
175
+ return {"result": eval(expr)}
176
+
177
+ executor = PipelineExecutor(
178
+ model=ModelClient(model="gpt-4o-mini", api_key="sk-..."),
179
+ tools=[calc],
180
+ )
181
+ result = await executor.run("What is 42 * 17?")
182
+ ```
183
+
184
+ ## Events
185
+
186
+ ```python
187
+ async for event in runtime.execute(workflow_data, input_data):
188
+ match event["type"]:
189
+ case "workflow_start": ... # node count, edge count
190
+ case "execution_plan": ... # order, node_count
191
+ case "node_start": ... # node_id, function_id, name, input_keys, timestamp
192
+ case "node_complete": ... # node_id, result_keys, output_data, duration_ms
193
+ case "node_skip": ... # node_id, reason (excluded/bypass)
194
+ case "route_decision": ... # selected_port
195
+ case "stream_chunk": ... # node_id, chunk
196
+ case "agent_tool_call": ... # node_id, tool_name, args
197
+ case "agent_tool_result":... # node_id, tool_name, result
198
+ case "workflow_complete": ... # final results
199
+ case "workflow_error": ... # error detail
200
+ ```
201
+
202
+ ## Package Structure
203
+
204
+ ```
205
+ mantis/
206
+ ├── runtime/ # WorkflowRuntime (core engine)
207
+ │ ├── runtime.py # entry point: PARSE → PLAN → EXECUTE → FINALIZE
208
+ │ ├── parse.py # workflow JSON → NodeInfo/EdgeInfo
209
+ │ ├── plan.py # topological sort (Kahn's algorithm)
210
+ │ ├── execute.py # main loop + event emission
211
+ │ ├── port_store.py # port-based data wiring
212
+ │ ├── stream_manager.py # generator fan-out / BufferedFactory
213
+ │ ├── route_resolver.py # router branching + DFS exclusion
214
+ │ ├── runners/ # NodeRunner handlers (6 types)
215
+ │ └── hooks/ # ExecutionHook (trace, approval)
216
+
217
+ ├── executor/ # PipelineExecutor (agent mode)
218
+ │ ├── pipeline.py # PREPARE → RESOLVE → ACT → OBSERVE loop
219
+ │ ├── flow.py # FlowState for deterministic flows
220
+ │ └── phases/ # pluggable phase implementations
221
+
222
+ ├── tools/ # @tool decorator, ToolRegistry, bridge
223
+ ├── search/ # GraphToolManager (graph-tool-call)
224
+ ├── sandbox/ # DockerSandbox
225
+ ├── generate/ # ToolGenerator (LLM → code → verify → register)
226
+ ├── testing/ # ToolTester (schema → smoke → pytest)
227
+ ├── workflow/ # WorkflowDef, Store, Generator
228
+ ├── adapters/ # SSE, xgen, LangChain adapters
229
+ ├── models/ # NodeInfo, PortInfo, EdgeInfo
230
+ └── exceptions.py # MantisError hierarchy
231
+ ```
232
+
233
+ ## License
234
+
235
+ MIT