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.
- mantis_engine-0.6.0/.github/workflows/publish.yml +27 -0
- mantis_engine-0.6.0/.gitignore +13 -0
- mantis_engine-0.6.0/LICENSE +21 -0
- mantis_engine-0.6.0/PKG-INFO +271 -0
- mantis_engine-0.6.0/README.md +235 -0
- mantis_engine-0.6.0/examples/demo.py +241 -0
- mantis_engine-0.6.0/examples/demo_langchain.py +384 -0
- mantis_engine-0.6.0/examples/demo_live.py +386 -0
- mantis_engine-0.6.0/examples/demo_runtime.py +400 -0
- mantis_engine-0.6.0/examples/demo_runtime_full.py +265 -0
- mantis_engine-0.6.0/mantis/__init__.py +75 -0
- mantis_engine-0.6.0/mantis/__main__.py +59 -0
- mantis_engine-0.6.0/mantis/adapters/__init__.py +10 -0
- mantis_engine-0.6.0/mantis/adapters/canvas_adapter.py +293 -0
- mantis_engine-0.6.0/mantis/adapters/langchain_adapter.py +143 -0
- mantis_engine-0.6.0/mantis/adapters/sse_adapter.py +163 -0
- mantis_engine-0.6.0/mantis/adapters/xgen_adapter.py +129 -0
- mantis_engine-0.6.0/mantis/context/__init__.py +0 -0
- mantis_engine-0.6.0/mantis/context/conversation.py +95 -0
- mantis_engine-0.6.0/mantis/exceptions.py +43 -0
- mantis_engine-0.6.0/mantis/executor/__init__.py +15 -0
- mantis_engine-0.6.0/mantis/executor/flow.py +128 -0
- mantis_engine-0.6.0/mantis/executor/phases/__init__.py +16 -0
- mantis_engine-0.6.0/mantis/executor/phases/act.py +116 -0
- mantis_engine-0.6.0/mantis/executor/phases/observe.py +33 -0
- mantis_engine-0.6.0/mantis/executor/phases/prepare.py +40 -0
- mantis_engine-0.6.0/mantis/executor/phases/protocol.py +26 -0
- mantis_engine-0.6.0/mantis/executor/phases/resolve.py +58 -0
- mantis_engine-0.6.0/mantis/executor/pipeline.py +579 -0
- mantis_engine-0.6.0/mantis/executor/protocol.py +74 -0
- mantis_engine-0.6.0/mantis/generate/__init__.py +8 -0
- mantis_engine-0.6.0/mantis/generate/tool_generator.py +347 -0
- mantis_engine-0.6.0/mantis/llm/__init__.py +13 -0
- mantis_engine-0.6.0/mantis/llm/openai_provider.py +132 -0
- mantis_engine-0.6.0/mantis/llm/protocol.py +43 -0
- mantis_engine-0.6.0/mantis/middleware/__init__.py +14 -0
- mantis_engine-0.6.0/mantis/middleware/approval.py +83 -0
- mantis_engine-0.6.0/mantis/middleware/base.py +87 -0
- mantis_engine-0.6.0/mantis/middleware/graph_search.py +168 -0
- mantis_engine-0.6.0/mantis/middleware/state.py +125 -0
- mantis_engine-0.6.0/mantis/middleware/trace.py +78 -0
- mantis_engine-0.6.0/mantis/models/__init__.py +1 -0
- mantis_engine-0.6.0/mantis/models/node.py +38 -0
- mantis_engine-0.6.0/mantis/providers/__init__.py +30 -0
- mantis_engine-0.6.0/mantis/runtime/__init__.py +5 -0
- mantis_engine-0.6.0/mantis/runtime/context.py +44 -0
- mantis_engine-0.6.0/mantis/runtime/hooks/__init__.py +1 -0
- mantis_engine-0.6.0/mantis/runtime/hooks/protocol.py +12 -0
- mantis_engine-0.6.0/mantis/runtime/hooks/trace.py +27 -0
- mantis_engine-0.6.0/mantis/runtime/parse.py +121 -0
- mantis_engine-0.6.0/mantis/runtime/plan.py +60 -0
- mantis_engine-0.6.0/mantis/runtime/port_store.py +87 -0
- mantis_engine-0.6.0/mantis/runtime/route_resolver.py +131 -0
- mantis_engine-0.6.0/mantis/runtime/runners/__init__.py +1 -0
- mantis_engine-0.6.0/mantis/runtime/runners/agent.py +137 -0
- mantis_engine-0.6.0/mantis/runtime/runners/bypass.py +29 -0
- mantis_engine-0.6.0/mantis/runtime/runners/default.py +28 -0
- mantis_engine-0.6.0/mantis/runtime/runners/io.py +35 -0
- mantis_engine-0.6.0/mantis/runtime/runners/protocol.py +10 -0
- mantis_engine-0.6.0/mantis/runtime/runners/provider.py +29 -0
- mantis_engine-0.6.0/mantis/runtime/runners/router.py +38 -0
- mantis_engine-0.6.0/mantis/runtime/runtime.py +283 -0
- mantis_engine-0.6.0/mantis/runtime/stream_manager.py +62 -0
- mantis_engine-0.6.0/mantis/safety/__init__.py +0 -0
- mantis_engine-0.6.0/mantis/safety/approval.py +151 -0
- mantis_engine-0.6.0/mantis/sandbox/__init__.py +11 -0
- mantis_engine-0.6.0/mantis/sandbox/runner.py +89 -0
- mantis_engine-0.6.0/mantis/sandbox/sandbox.py +204 -0
- mantis_engine-0.6.0/mantis/sandbox/tools.py +98 -0
- mantis_engine-0.6.0/mantis/search/__init__.py +18 -0
- mantis_engine-0.6.0/mantis/search/graph_search.py +820 -0
- mantis_engine-0.6.0/mantis/state/__init__.py +5 -0
- mantis_engine-0.6.0/mantis/state/store.py +113 -0
- mantis_engine-0.6.0/mantis/state/tools.py +87 -0
- mantis_engine-0.6.0/mantis/testing/__init__.py +10 -0
- mantis_engine-0.6.0/mantis/testing/dummy_args.py +41 -0
- mantis_engine-0.6.0/mantis/testing/pytest_runner.py +72 -0
- mantis_engine-0.6.0/mantis/testing/tool_tester.py +155 -0
- mantis_engine-0.6.0/mantis/tools/__init__.py +12 -0
- mantis_engine-0.6.0/mantis/tools/bridge.py +57 -0
- mantis_engine-0.6.0/mantis/tools/builtins.py +43 -0
- mantis_engine-0.6.0/mantis/tools/decorator.py +96 -0
- mantis_engine-0.6.0/mantis/tools/meta.py +124 -0
- mantis_engine-0.6.0/mantis/tools/registry.py +199 -0
- mantis_engine-0.6.0/mantis/trace/__init__.py +0 -0
- mantis_engine-0.6.0/mantis/trace/collector.py +107 -0
- mantis_engine-0.6.0/mantis/trace/exporter.py +27 -0
- mantis_engine-0.6.0/mantis/workflow/__init__.py +20 -0
- mantis_engine-0.6.0/mantis/workflow/generator.py +225 -0
- mantis_engine-0.6.0/mantis/workflow/models.py +79 -0
- mantis_engine-0.6.0/mantis/workflow/runner.py +259 -0
- mantis_engine-0.6.0/mantis/workflow/store.py +37 -0
- mantis_engine-0.6.0/mantis/workflow/tools.py +266 -0
- mantis_engine-0.6.0/pyproject.toml +55 -0
- mantis_engine-0.6.0/tests/test_agent.py +368 -0
- mantis_engine-0.6.0/tests/test_approval.py +63 -0
- mantis_engine-0.6.0/tests/test_context.py +74 -0
- mantis_engine-0.6.0/tests/test_engine_integration.py +521 -0
- mantis_engine-0.6.0/tests/test_generator.py +113 -0
- mantis_engine-0.6.0/tests/test_graph_tool.py +447 -0
- mantis_engine-0.6.0/tests/test_model_client.py +121 -0
- mantis_engine-0.6.0/tests/test_phases.py +186 -0
- mantis_engine-0.6.0/tests/test_registry_v2.py +175 -0
- mantis_engine-0.6.0/tests/test_runtime.py +382 -0
- mantis_engine-0.6.0/tests/test_sandbox.py +34 -0
- mantis_engine-0.6.0/tests/test_sandbox_tools.py +54 -0
- mantis_engine-0.6.0/tests/test_tools.py +68 -0
- mantis_engine-0.6.0/tests/test_trace.py +68 -0
- mantis_engine-0.6.0/tests/test_v3_integration.py +497 -0
- 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,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
|