neuralosd 1.0.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.
- neuralosd-1.0.0/PKG-INFO +151 -0
- neuralosd-1.0.0/README.md +114 -0
- neuralosd-1.0.0/pyproject.toml +44 -0
- neuralosd-1.0.0/setup.cfg +4 -0
- neuralosd-1.0.0/src/neuralosd/__init__.py +24 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/__init__.py +0 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/ask.py +7 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/calibrate.py +13 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/diff.py +13 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/gaps.py +13 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/golden.py +13 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/init.py +21 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/invariants.py +13 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/lint.py +8 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/openapi.py +13 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/serve.py +5 -0
- neuralosd-1.0.0/src/neuralosd/_cmd/truth.py +13 -0
- neuralosd-1.0.0/src/neuralosd/adapters.py +54 -0
- neuralosd-1.0.0/src/neuralosd/backends/__init__.py +15 -0
- neuralosd-1.0.0/src/neuralosd/backends/boxlite_backend.py +69 -0
- neuralosd-1.0.0/src/neuralosd/backends/msb_backend.py +58 -0
- neuralosd-1.0.0/src/neuralosd/chains.py +97 -0
- neuralosd-1.0.0/src/neuralosd/cli.py +147 -0
- neuralosd-1.0.0/src/neuralosd/engine.py +82 -0
- neuralosd-1.0.0/src/neuralosd/hitl.py +46 -0
- neuralosd-1.0.0/src/neuralosd/instance.py +99 -0
- neuralosd-1.0.0/src/neuralosd/lint.py +46 -0
- neuralosd-1.0.0/src/neuralosd/mcp.py +99 -0
- neuralosd-1.0.0/src/neuralosd/memory.py +51 -0
- neuralosd-1.0.0/src/neuralosd/meta.py +57 -0
- neuralosd-1.0.0/src/neuralosd/metrics.py +27 -0
- neuralosd-1.0.0/src/neuralosd/probe.py +74 -0
- neuralosd-1.0.0/src/neuralosd/registry.py +44 -0
- neuralosd-1.0.0/src/neuralosd/router.py +376 -0
- neuralosd-1.0.0/src/neuralosd/scheduler.py +86 -0
- neuralosd-1.0.0/src/neuralosd/service.py +57 -0
- neuralosd-1.0.0/src/neuralosd.egg-info/PKG-INFO +151 -0
- neuralosd-1.0.0/src/neuralosd.egg-info/SOURCES.txt +45 -0
- neuralosd-1.0.0/src/neuralosd.egg-info/dependency_links.txt +1 -0
- neuralosd-1.0.0/src/neuralosd.egg-info/entry_points.txt +2 -0
- neuralosd-1.0.0/src/neuralosd.egg-info/requires.txt +22 -0
- neuralosd-1.0.0/src/neuralosd.egg-info/top_level.txt +1 -0
- neuralosd-1.0.0/tests/test_backends.py +23 -0
- neuralosd-1.0.0/tests/test_framework.py +134 -0
- neuralosd-1.0.0/tests/test_integration.py +109 -0
- neuralosd-1.0.0/tests/test_probe.py +76 -0
- neuralosd-1.0.0/tests/test_router.py +97 -0
neuralosd-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: neuralosd
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Deterministic-first agentic runtime — @probe framework, chain runner, meta-selector, HITL guardrails, MCP/OpenAPI exposure, and microVM sandbox backends (BoxLite / Microsandbox)
|
|
5
|
+
Author-email: Hyperspace Technologies <agent@superagent.ng>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: neuralos,agentic,sandbox,microvm,on-device,tool-calling,deterministic,offline,probes
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: MacOS
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
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 :: Python Modules
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Provides-Extra: model
|
|
22
|
+
Requires-Dist: neuralos>=3.0.3; extra == "model"
|
|
23
|
+
Provides-Extra: boxlite
|
|
24
|
+
Requires-Dist: boxlite>=0.10.0; extra == "boxlite"
|
|
25
|
+
Provides-Extra: msb
|
|
26
|
+
Provides-Extra: data
|
|
27
|
+
Requires-Dist: pydantic>=2; extra == "data"
|
|
28
|
+
Requires-Dist: pymysql; extra == "data"
|
|
29
|
+
Provides-Extra: all
|
|
30
|
+
Requires-Dist: neuralos>=3.0.3; extra == "all"
|
|
31
|
+
Requires-Dist: boxlite>=0.10.0; extra == "all"
|
|
32
|
+
Requires-Dist: pydantic>=2; extra == "all"
|
|
33
|
+
Requires-Dist: pymysql; extra == "all"
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
37
|
+
|
|
38
|
+
# neuralosd — deterministic-first agentic runtime
|
|
39
|
+
|
|
40
|
+
**One pip install. Two sandbox backends. Any data source. Offline. On CPU.**
|
|
41
|
+
|
|
42
|
+
`neuralosd` is the agentic runtime for [neuralOS](https://neuralos.ng) — the
|
|
43
|
+
121M-parameter on-device tool-calling model. It turns any data source into a
|
|
44
|
+
private, offline question-answering service inside hardware-isolated microVMs.
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
pip install neuralosd[all]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## What it does
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
YOUR DATA (CSV, DB, API, logs) THE RUNTIME THE RESULT
|
|
54
|
+
────────────────────── ────────────── ──────────────
|
|
55
|
+
profile → model → generate → @probe + router → ask in English,
|
|
56
|
+
→ chains + guardrails get verified
|
|
57
|
+
→ serve + monitor answers, offline
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Two sandbox backends
|
|
61
|
+
|
|
62
|
+
| Backend | Install | Best for |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| **BoxLite** | `pip install neuralosd[boxlite]` | persistent service boxes, CoW clones, port publication |
|
|
65
|
+
| **Microsandbox** | `pip install neuralosd[msb]` | live RAM snapshots, CoW forks, declarative recreate |
|
|
66
|
+
|
|
67
|
+
Both run the same probe contract. Switch by configuration, not code.
|
|
68
|
+
|
|
69
|
+
## Quick start
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# 1. Install
|
|
73
|
+
pip install neuralosd[all]
|
|
74
|
+
|
|
75
|
+
# 2. Build an instance from your data
|
|
76
|
+
neuralosd build --source your_data.csv --name my-analyst
|
|
77
|
+
|
|
78
|
+
# 3. Deploy into a sandbox
|
|
79
|
+
neuralosd deploy --name my-analyst --backend boxlite
|
|
80
|
+
|
|
81
|
+
# 4. Ask
|
|
82
|
+
neuralosd ask --instance my-analyst "how many records"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## The @probe framework
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from neuralosd import probe, Instance
|
|
89
|
+
|
|
90
|
+
@probe(
|
|
91
|
+
description="Customers ranked by lifetime spend",
|
|
92
|
+
triggers=["top customers", "best customers"],
|
|
93
|
+
args={"limit": {"type": "integer", "min": 1, "max": 25, "default": 10}},
|
|
94
|
+
pii=["email"],
|
|
95
|
+
)
|
|
96
|
+
def top_customers(limit: int = 10):
|
|
97
|
+
return db.query("SELECT ... LIMIT %s", (limit,))
|
|
98
|
+
|
|
99
|
+
inst = Instance(name="my-analyst", probes=[top_customers])
|
|
100
|
+
env = inst.ask("top customers")
|
|
101
|
+
print(env["results"])
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Chains (multi-step answers)
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from neuralosd import chain, ChainRunner
|
|
108
|
+
|
|
109
|
+
@chain(name="genre_deep_dive", steps=[
|
|
110
|
+
{"probe": "top_genres"},
|
|
111
|
+
{"probe": "tracks_by_genre", "args": {"genre": "{{step_0.genre}}"}},
|
|
112
|
+
])
|
|
113
|
+
def genre_deep_dive(ctx): ...
|
|
114
|
+
|
|
115
|
+
runner = ChainRunner(probes_by_name)
|
|
116
|
+
runner.register("genre_deep_dive", genre_deep_dive._chain_steps)
|
|
117
|
+
result = runner.run("genre_deep_dive")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Meta-selector (multi-instance routing)
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from neuralosd import MetaSelector
|
|
124
|
+
|
|
125
|
+
selector = MetaSelector({
|
|
126
|
+
"finance": {"description": "revenue, invoices, customers"},
|
|
127
|
+
"ops": {"description": "incidents, uptime, alerts"},
|
|
128
|
+
})
|
|
129
|
+
instance = selector.route("how many open incidents") # → "ops"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## HITL guardrails
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from neuralosd import ConfirmStore
|
|
136
|
+
store = ConfirmStore()
|
|
137
|
+
pending = store.create(probe_fn, args, reason="low confidence")
|
|
138
|
+
# ... human confirms ...
|
|
139
|
+
result = store.confirm(pending["confirm_token"])
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## MCP server (expose probes to any AI agent)
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
python3 -m neuralosd.mcp /path/to/instance
|
|
146
|
+
# or add to Claude Desktop / Cursor MCP config
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# neuralosd — deterministic-first agentic runtime
|
|
2
|
+
|
|
3
|
+
**One pip install. Two sandbox backends. Any data source. Offline. On CPU.**
|
|
4
|
+
|
|
5
|
+
`neuralosd` is the agentic runtime for [neuralOS](https://neuralos.ng) — the
|
|
6
|
+
121M-parameter on-device tool-calling model. It turns any data source into a
|
|
7
|
+
private, offline question-answering service inside hardware-isolated microVMs.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
pip install neuralosd[all]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
YOUR DATA (CSV, DB, API, logs) THE RUNTIME THE RESULT
|
|
17
|
+
────────────────────── ────────────── ──────────────
|
|
18
|
+
profile → model → generate → @probe + router → ask in English,
|
|
19
|
+
→ chains + guardrails get verified
|
|
20
|
+
→ serve + monitor answers, offline
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Two sandbox backends
|
|
24
|
+
|
|
25
|
+
| Backend | Install | Best for |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| **BoxLite** | `pip install neuralosd[boxlite]` | persistent service boxes, CoW clones, port publication |
|
|
28
|
+
| **Microsandbox** | `pip install neuralosd[msb]` | live RAM snapshots, CoW forks, declarative recreate |
|
|
29
|
+
|
|
30
|
+
Both run the same probe contract. Switch by configuration, not code.
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# 1. Install
|
|
36
|
+
pip install neuralosd[all]
|
|
37
|
+
|
|
38
|
+
# 2. Build an instance from your data
|
|
39
|
+
neuralosd build --source your_data.csv --name my-analyst
|
|
40
|
+
|
|
41
|
+
# 3. Deploy into a sandbox
|
|
42
|
+
neuralosd deploy --name my-analyst --backend boxlite
|
|
43
|
+
|
|
44
|
+
# 4. Ask
|
|
45
|
+
neuralosd ask --instance my-analyst "how many records"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## The @probe framework
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from neuralosd import probe, Instance
|
|
52
|
+
|
|
53
|
+
@probe(
|
|
54
|
+
description="Customers ranked by lifetime spend",
|
|
55
|
+
triggers=["top customers", "best customers"],
|
|
56
|
+
args={"limit": {"type": "integer", "min": 1, "max": 25, "default": 10}},
|
|
57
|
+
pii=["email"],
|
|
58
|
+
)
|
|
59
|
+
def top_customers(limit: int = 10):
|
|
60
|
+
return db.query("SELECT ... LIMIT %s", (limit,))
|
|
61
|
+
|
|
62
|
+
inst = Instance(name="my-analyst", probes=[top_customers])
|
|
63
|
+
env = inst.ask("top customers")
|
|
64
|
+
print(env["results"])
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Chains (multi-step answers)
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from neuralosd import chain, ChainRunner
|
|
71
|
+
|
|
72
|
+
@chain(name="genre_deep_dive", steps=[
|
|
73
|
+
{"probe": "top_genres"},
|
|
74
|
+
{"probe": "tracks_by_genre", "args": {"genre": "{{step_0.genre}}"}},
|
|
75
|
+
])
|
|
76
|
+
def genre_deep_dive(ctx): ...
|
|
77
|
+
|
|
78
|
+
runner = ChainRunner(probes_by_name)
|
|
79
|
+
runner.register("genre_deep_dive", genre_deep_dive._chain_steps)
|
|
80
|
+
result = runner.run("genre_deep_dive")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Meta-selector (multi-instance routing)
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from neuralosd import MetaSelector
|
|
87
|
+
|
|
88
|
+
selector = MetaSelector({
|
|
89
|
+
"finance": {"description": "revenue, invoices, customers"},
|
|
90
|
+
"ops": {"description": "incidents, uptime, alerts"},
|
|
91
|
+
})
|
|
92
|
+
instance = selector.route("how many open incidents") # → "ops"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## HITL guardrails
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from neuralosd import ConfirmStore
|
|
99
|
+
store = ConfirmStore()
|
|
100
|
+
pending = store.create(probe_fn, args, reason="low confidence")
|
|
101
|
+
# ... human confirms ...
|
|
102
|
+
result = store.confirm(pending["confirm_token"])
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## MCP server (expose probes to any AI agent)
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
python3 -m neuralosd.mcp /path/to/instance
|
|
109
|
+
# or add to Claude Desktop / Cursor MCP config
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "neuralosd"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Deterministic-first agentic runtime — @probe framework, chain runner, meta-selector, HITL guardrails, MCP/OpenAPI exposure, and microVM sandbox backends (BoxLite / Microsandbox)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Hyperspace Technologies", email = "agent@superagent.ng" }]
|
|
13
|
+
keywords = ["neuralos", "agentic", "sandbox", "microvm", "on-device",
|
|
14
|
+
"tool-calling", "deterministic", "offline", "probes"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 5 - Production/Stable",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: MacOS",
|
|
20
|
+
"Operating System :: POSIX :: Linux",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
26
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.scripts]
|
|
30
|
+
neuralosd = "neuralosd.cli:main"
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
model = ["neuralos>=3.0.3"]
|
|
34
|
+
boxlite = ["boxlite>=0.10.0"]
|
|
35
|
+
msb = []
|
|
36
|
+
data = ["pydantic>=2", "pymysql"]
|
|
37
|
+
all = ["neuralos>=3.0.3", "boxlite>=0.10.0", "pydantic>=2", "pymysql"]
|
|
38
|
+
dev = ["pytest", "pytest-asyncio"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["src"]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""neuralosd — deterministic-first agentic runtime for neuralOS.
|
|
2
|
+
|
|
3
|
+
One pip install. Two sandbox backends (BoxLite, Microsandbox). Any data
|
|
4
|
+
source. Offline. On CPU. With verification gates, audit trail, and
|
|
5
|
+
deterministic routing.
|
|
6
|
+
"""
|
|
7
|
+
__version__ = "1.0.0"
|
|
8
|
+
|
|
9
|
+
from .probe import probe, enum_arg, pattern_arg, int_arg, ProbeMeta
|
|
10
|
+
from .router import Router, NoResults
|
|
11
|
+
from .instance import Instance
|
|
12
|
+
from .chains import ChainRunner, chain, ChainError
|
|
13
|
+
from .meta import MetaSelector
|
|
14
|
+
from .hitl import ConfirmStore, ConfirmRequired
|
|
15
|
+
from .lint import TriggerLinter
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"probe", "enum_arg", "pattern_arg", "int_arg", "ProbeMeta",
|
|
19
|
+
"Router", "NoResults", "Instance",
|
|
20
|
+
"ChainRunner", "chain", "ChainError",
|
|
21
|
+
"MetaSelector",
|
|
22
|
+
"ConfirmStore", "ConfirmRequired",
|
|
23
|
+
"TriggerLinter",
|
|
24
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
def run(a):
|
|
2
|
+
import sys, subprocess, os
|
|
3
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
4
|
+
"..", "tools", "calibrate.py")
|
|
5
|
+
if not os.path.exists(script):
|
|
6
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
7
|
+
"..", "..", "..",
|
|
8
|
+
"agent-skills", "skills", "neuralos", "scripts",
|
|
9
|
+
"calibrate.py")
|
|
10
|
+
if os.path.exists(script):
|
|
11
|
+
raise SystemExit(subprocess.call([sys.executable, script] + sys.argv[1:]))
|
|
12
|
+
print(f"tool script not found: {script}")
|
|
13
|
+
raise SystemExit(1)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
def run(a):
|
|
2
|
+
import sys, subprocess, os
|
|
3
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
4
|
+
"..", "tools", "diff.py")
|
|
5
|
+
if not os.path.exists(script):
|
|
6
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
7
|
+
"..", "..", "..",
|
|
8
|
+
"agent-skills", "skills", "neuralos", "scripts",
|
|
9
|
+
"diff.py")
|
|
10
|
+
if os.path.exists(script):
|
|
11
|
+
raise SystemExit(subprocess.call([sys.executable, script] + sys.argv[1:]))
|
|
12
|
+
print(f"tool script not found: {script}")
|
|
13
|
+
raise SystemExit(1)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
def run(a):
|
|
2
|
+
import sys, subprocess, os
|
|
3
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
4
|
+
"..", "tools", "gaps.py")
|
|
5
|
+
if not os.path.exists(script):
|
|
6
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
7
|
+
"..", "..", "..",
|
|
8
|
+
"agent-skills", "skills", "neuralos", "scripts",
|
|
9
|
+
"gaps.py")
|
|
10
|
+
if os.path.exists(script):
|
|
11
|
+
raise SystemExit(subprocess.call([sys.executable, script] + sys.argv[1:]))
|
|
12
|
+
print(f"tool script not found: {script}")
|
|
13
|
+
raise SystemExit(1)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
def run(a):
|
|
2
|
+
import sys, subprocess, os
|
|
3
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
4
|
+
"..", "tools", "golden.py")
|
|
5
|
+
if not os.path.exists(script):
|
|
6
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
7
|
+
"..", "..", "..",
|
|
8
|
+
"agent-skills", "skills", "neuralos", "scripts",
|
|
9
|
+
"golden.py")
|
|
10
|
+
if os.path.exists(script):
|
|
11
|
+
raise SystemExit(subprocess.call([sys.executable, script] + sys.argv[1:]))
|
|
12
|
+
print(f"tool script not found: {script}")
|
|
13
|
+
raise SystemExit(1)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
def run(a):
|
|
2
|
+
import subprocess, sys, os
|
|
3
|
+
out = a.out or f"./{a.name}"
|
|
4
|
+
scripts = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
5
|
+
"..", "..", "..", "..",
|
|
6
|
+
"agent-skills", "skills", "neuralos", "scripts")
|
|
7
|
+
profile = os.path.join(out, "profile.json")
|
|
8
|
+
os.makedirs(out, exist_ok=True)
|
|
9
|
+
for step in [
|
|
10
|
+
[sys.executable, os.path.join(scripts, "profile_data.py"),
|
|
11
|
+
"--source", a.source, "--out", profile],
|
|
12
|
+
[sys.executable, os.path.join(scripts, "gen_pydantic.py"),
|
|
13
|
+
"--profile", profile, "--out", os.path.join(out, "models.py")],
|
|
14
|
+
[sys.executable, os.path.join(scripts, "gen_needle_instance.py"),
|
|
15
|
+
"--profile", profile, "--models", os.path.join(out, "models.py"),
|
|
16
|
+
"--out", out, "--agent-name", a.name],
|
|
17
|
+
]:
|
|
18
|
+
r = subprocess.run(step, capture_output=True, text=True)
|
|
19
|
+
if r.returncode:
|
|
20
|
+
print(r.stderr[-500:]); raise SystemExit(r.returncode)
|
|
21
|
+
print(f"instance '{a.name}' generated at {out}")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
def run(a):
|
|
2
|
+
import sys, subprocess, os
|
|
3
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
4
|
+
"..", "tools", "invariants.py")
|
|
5
|
+
if not os.path.exists(script):
|
|
6
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
7
|
+
"..", "..", "..",
|
|
8
|
+
"agent-skills", "skills", "neuralos", "scripts",
|
|
9
|
+
"invariants.py")
|
|
10
|
+
if os.path.exists(script):
|
|
11
|
+
raise SystemExit(subprocess.call([sys.executable, script] + sys.argv[1:]))
|
|
12
|
+
print(f"tool script not found: {script}")
|
|
13
|
+
raise SystemExit(1)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import json, sys
|
|
2
|
+
def run(a):
|
|
3
|
+
from neuralosd.lint import TriggerLinter
|
|
4
|
+
menu = json.load(open(a.menu))
|
|
5
|
+
r = TriggerLinter(menu, top=a.top).lint()
|
|
6
|
+
for h in r["hard"]: print(f"HARD {h}")
|
|
7
|
+
for s in r["soft"]: print(f"soft {s}")
|
|
8
|
+
raise SystemExit(1 if r["hard"] else 0)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
def run(a):
|
|
2
|
+
import sys, subprocess, os
|
|
3
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
4
|
+
"..", "tools", "openapi.py")
|
|
5
|
+
if not os.path.exists(script):
|
|
6
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
7
|
+
"..", "..", "..",
|
|
8
|
+
"agent-skills", "skills", "neuralos", "scripts",
|
|
9
|
+
"openapi.py")
|
|
10
|
+
if os.path.exists(script):
|
|
11
|
+
raise SystemExit(subprocess.call([sys.executable, script] + sys.argv[1:]))
|
|
12
|
+
print(f"tool script not found: {script}")
|
|
13
|
+
raise SystemExit(1)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
def run(a):
|
|
2
|
+
import sys, subprocess, os
|
|
3
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
4
|
+
"..", "tools", "truth.py")
|
|
5
|
+
if not os.path.exists(script):
|
|
6
|
+
script = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
7
|
+
"..", "..", "..",
|
|
8
|
+
"agent-skills", "skills", "neuralos", "scripts",
|
|
9
|
+
"truth.py")
|
|
10
|
+
if os.path.exists(script):
|
|
11
|
+
raise SystemExit(subprocess.call([sys.executable, script] + sys.argv[1:]))
|
|
12
|
+
print(f"tool script not found: {script}")
|
|
13
|
+
raise SystemExit(1)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Sandbox adapters — route probe execution to BoxLite / MSB sandboxes.
|
|
2
|
+
|
|
3
|
+
An adapter implements the same interface as an in-process probe: takes
|
|
4
|
+
kwargs, returns a result dict. The instance's tier declaration routes
|
|
5
|
+
execution to the right substrate.
|
|
6
|
+
"""
|
|
7
|
+
import json
|
|
8
|
+
import urllib.request
|
|
9
|
+
from typing import Any, Dict
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BoxLiteAdapter:
|
|
13
|
+
"""Executes probes inside a BoxLite box via `boxlite serve` REST."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, serve_url: str = "http://localhost:8100",
|
|
16
|
+
api_key: str = None, box_name: str = "chinook-1gb"):
|
|
17
|
+
self.serve_url = serve_url.rstrip("/")
|
|
18
|
+
self.api_key = api_key
|
|
19
|
+
self.box_name = box_name
|
|
20
|
+
|
|
21
|
+
def exec_probe(self, code: str) -> Dict:
|
|
22
|
+
"""Run a Python snippet inside the sandbox."""
|
|
23
|
+
req = urllib.request.Request(
|
|
24
|
+
f"{self.serve_url}/v1/boxes/{self.box_name}/exec",
|
|
25
|
+
data=json.dumps({"command": code}).encode(),
|
|
26
|
+
headers={"Content-Type": "application/json",
|
|
27
|
+
**({"Authorization": f"Bearer {self.api_key}"}
|
|
28
|
+
if self.api_key else {})})
|
|
29
|
+
r = urllib.request.urlopen(req, timeout=120)
|
|
30
|
+
return json.loads(r.read())
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class MSBAdapter:
|
|
34
|
+
"""Executes probes inside a Microsandbox via `msb exec`."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, sandbox_name: str = "chinook-ms"):
|
|
37
|
+
self.sandbox_name = sandbox_name
|
|
38
|
+
|
|
39
|
+
def exec_probe(self, code: str) -> Dict:
|
|
40
|
+
import subprocess
|
|
41
|
+
r = subprocess.run(
|
|
42
|
+
["msb", "exec", self.sandbox_name, "--", "python3", "-c", code],
|
|
43
|
+
capture_output=True, text=True, timeout=120)
|
|
44
|
+
return {"stdout": r.stdout, "stderr": r.stderr,
|
|
45
|
+
"exit_code": r.returncode}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_adapter(tier: str, **kwargs) -> Any:
|
|
49
|
+
"""Factory: tier string → adapter instance, or None for in-process."""
|
|
50
|
+
if tier.startswith("sandbox:boxlite:"):
|
|
51
|
+
return BoxLiteAdapter(box_name=tier.split(":")[-1], **kwargs)
|
|
52
|
+
if tier.startswith("sandbox:msb:"):
|
|
53
|
+
return MSBAdapter(sandbox_name=tier.split(":")[-1], **kwargs)
|
|
54
|
+
return None # in-process
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .boxlite_backend import BoxLiteBackend
|
|
2
|
+
from .msb_backend import MSBBackend
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_backend(name: str, **kwargs):
|
|
6
|
+
if name == "boxlite":
|
|
7
|
+
return BoxLiteBackend(**kwargs)
|
|
8
|
+
if name == "msb":
|
|
9
|
+
return MSBBackend(**kwargs)
|
|
10
|
+
raise ValueError(f"unknown backend: {name}")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def available_backends():
|
|
14
|
+
return {"boxlite": BoxLiteBackend.available(),
|
|
15
|
+
"msb": MSBBackend.available()}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""BoxLite backend — lifecycle management for BoxLite microVM boxes."""
|
|
2
|
+
import asyncio
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BoxLiteBackend:
|
|
7
|
+
"""Manages BoxLite box lifecycle for neuralOS instances."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, serve_url: str = None, api_key: str = None):
|
|
10
|
+
self.serve_url = serve_url
|
|
11
|
+
self.api_key = api_key
|
|
12
|
+
|
|
13
|
+
def _rt(self):
|
|
14
|
+
import boxlite
|
|
15
|
+
if self.serve_url:
|
|
16
|
+
cred = boxlite.ApiKeyCredential(self.api_key) if self.api_key else None
|
|
17
|
+
return boxlite.Boxlite.rest(
|
|
18
|
+
boxlite.BoxliteRestOptions(url=self.serve_url, credential=cred))
|
|
19
|
+
return boxlite.Boxlite.default()
|
|
20
|
+
|
|
21
|
+
async def create(self, name: str, image: str = "python:3.12-slim",
|
|
22
|
+
cpus: int = 1, memory_mib: int = 1024,
|
|
23
|
+
disk_gb: int = 6, **kwargs):
|
|
24
|
+
rt = self._rt()
|
|
25
|
+
got = await rt.get_or_create(
|
|
26
|
+
boxlite.BoxOptions(image=image, auto_delete=0,
|
|
27
|
+
cpus=cpus, memory_mib=memory_mib,
|
|
28
|
+
disk_size_gb=disk_gb, **kwargs),
|
|
29
|
+
name=name)
|
|
30
|
+
return got[0] if isinstance(got, tuple) else got
|
|
31
|
+
|
|
32
|
+
async def start(self, name: str):
|
|
33
|
+
box = await self._rt().get(name)
|
|
34
|
+
await box.start()
|
|
35
|
+
|
|
36
|
+
async def stop(self, name: str):
|
|
37
|
+
box = await self._rt().get(name)
|
|
38
|
+
await box.stop()
|
|
39
|
+
|
|
40
|
+
async def remove(self, name: str):
|
|
41
|
+
await self._rt().remove(name)
|
|
42
|
+
|
|
43
|
+
async def fork(self, template: str, name: str):
|
|
44
|
+
tpl = await self._rt().get(template)
|
|
45
|
+
return await tpl.clone_box(name=name)
|
|
46
|
+
|
|
47
|
+
async def exec(self, name: str, cmd: str, args: list = None):
|
|
48
|
+
box = await self._rt().get(name)
|
|
49
|
+
ex = await box.exec(cmd, args or [], timeout_secs=300)
|
|
50
|
+
out = []
|
|
51
|
+
async for line in ex.stdout(): out.append(line)
|
|
52
|
+
await ex.wait()
|
|
53
|
+
return "".join(out)
|
|
54
|
+
|
|
55
|
+
async def export(self, name: str, dest: str):
|
|
56
|
+
box = await self._rt().get(name)
|
|
57
|
+
await box.export(dest=dest)
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def available() -> bool:
|
|
61
|
+
try:
|
|
62
|
+
import boxlite
|
|
63
|
+
return True
|
|
64
|
+
except ImportError:
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def name() -> str:
|
|
69
|
+
return "boxlite"
|