nodus-sdk 0.1.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.
- nodus_sdk-0.1.0/LICENSE +21 -0
- nodus_sdk-0.1.0/PKG-INFO +231 -0
- nodus_sdk-0.1.0/README.md +136 -0
- nodus_sdk-0.1.0/nodus_sdk/__init__.py +12 -0
- nodus_sdk-0.1.0/nodus_sdk/_version.py +1 -0
- nodus_sdk-0.1.0/nodus_sdk/bridges/__init__.py +1 -0
- nodus_sdk-0.1.0/nodus_sdk/bridges/api.py +124 -0
- nodus_sdk-0.1.0/nodus_sdk/bridges/http.py +42 -0
- nodus_sdk-0.1.0/nodus_sdk/bridges/llm.py +44 -0
- nodus_sdk-0.1.0/nodus_sdk/bridges/observability.py +51 -0
- nodus_sdk-0.1.0/nodus_sdk/bridges/redis.py +41 -0
- nodus_sdk-0.1.0/nodus_sdk/bridges/scheduler.py +159 -0
- nodus_sdk-0.1.0/nodus_sdk/bridges/sql.py +103 -0
- nodus_sdk-0.1.0/nodus_sdk/bridges/vector.py +164 -0
- nodus_sdk-0.1.0/nodus_sdk/bridges/webhook.py +147 -0
- nodus_sdk-0.1.0/nodus_sdk/factory.py +88 -0
- nodus_sdk-0.1.0/nodus_sdk/runtime.py +175 -0
- nodus_sdk-0.1.0/nodus_sdk.egg-info/PKG-INFO +231 -0
- nodus_sdk-0.1.0/nodus_sdk.egg-info/SOURCES.txt +32 -0
- nodus_sdk-0.1.0/nodus_sdk.egg-info/dependency_links.txt +1 -0
- nodus_sdk-0.1.0/nodus_sdk.egg-info/requires.txt +70 -0
- nodus_sdk-0.1.0/nodus_sdk.egg-info/top_level.txt +1 -0
- nodus_sdk-0.1.0/pyproject.toml +70 -0
- nodus_sdk-0.1.0/setup.cfg +4 -0
- nodus_sdk-0.1.0/tests/test_bridges_api.py +150 -0
- nodus_sdk-0.1.0/tests/test_bridges_http.py +51 -0
- nodus_sdk-0.1.0/tests/test_bridges_llm.py +67 -0
- nodus_sdk-0.1.0/tests/test_bridges_observability.py +41 -0
- nodus_sdk-0.1.0/tests/test_bridges_redis.py +63 -0
- nodus_sdk-0.1.0/tests/test_bridges_scheduler.py +129 -0
- nodus_sdk-0.1.0/tests/test_bridges_sql.py +138 -0
- nodus_sdk-0.1.0/tests/test_bridges_vector.py +139 -0
- nodus_sdk-0.1.0/tests/test_bridges_webhook.py +121 -0
- nodus_sdk-0.1.0/tests/test_factory.py +180 -0
nodus_sdk-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shawn Knight
|
|
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.
|
nodus_sdk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nodus-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Unified platform SDK for Nodus — factory, bridges, and integration layer
|
|
5
|
+
Author: Shawn Knight
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Shawn Knight
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/Masterplanner25/nodus-sdk
|
|
29
|
+
Project-URL: Repository, https://github.com/Masterplanner25/nodus-sdk
|
|
30
|
+
Classifier: Development Status :: 3 - Alpha
|
|
31
|
+
Classifier: Intended Audience :: Developers
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
38
|
+
Requires-Python: >=3.10
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
License-File: LICENSE
|
|
41
|
+
Requires-Dist: nodus-lang>=4.0.0
|
|
42
|
+
Requires-Dist: nodus-schema>=0.1.0
|
|
43
|
+
Requires-Dist: nodus-protocol>=0.1.0
|
|
44
|
+
Requires-Dist: nodus-retry>=0.1.0
|
|
45
|
+
Provides-Extra: agent
|
|
46
|
+
Requires-Dist: nodus-agent>=0.1.0; extra == "agent"
|
|
47
|
+
Requires-Dist: nodus-state>=0.1.0; extra == "agent"
|
|
48
|
+
Requires-Dist: nodus-circuit-breaker>=0.1.0; extra == "agent"
|
|
49
|
+
Provides-Extra: workflow
|
|
50
|
+
Requires-Dist: nodus-workflow>=0.1.0; extra == "workflow"
|
|
51
|
+
Requires-Dist: nodus-state>=0.1.0; extra == "workflow"
|
|
52
|
+
Requires-Dist: nodus-events>=0.1.0; extra == "workflow"
|
|
53
|
+
Provides-Extra: memory
|
|
54
|
+
Requires-Dist: nodus-memory>=0.1.0; extra == "memory"
|
|
55
|
+
Provides-Extra: memory-ai
|
|
56
|
+
Requires-Dist: nodus-memory[openai]>=0.1.0; extra == "memory-ai"
|
|
57
|
+
Provides-Extra: auth
|
|
58
|
+
Requires-Dist: nodus-auth>=0.1.0; extra == "auth"
|
|
59
|
+
Provides-Extra: llm
|
|
60
|
+
Requires-Dist: nodus-llm>=0.1.0; extra == "llm"
|
|
61
|
+
Requires-Dist: nodus-circuit-breaker>=0.1.0; extra == "llm"
|
|
62
|
+
Provides-Extra: http
|
|
63
|
+
Requires-Dist: nodus-http>=0.1.0; extra == "http"
|
|
64
|
+
Provides-Extra: redis
|
|
65
|
+
Requires-Dist: nodus-queue[redis]>=0.1.0; extra == "redis"
|
|
66
|
+
Requires-Dist: nodus-events[redis]>=0.1.0; extra == "redis"
|
|
67
|
+
Provides-Extra: sql
|
|
68
|
+
Requires-Dist: sqlalchemy>=2.0; extra == "sql"
|
|
69
|
+
Provides-Extra: vector
|
|
70
|
+
Requires-Dist: pgvector>=0.2; extra == "vector"
|
|
71
|
+
Requires-Dist: sqlalchemy>=2.0; extra == "vector"
|
|
72
|
+
Provides-Extra: scheduler
|
|
73
|
+
Requires-Dist: apscheduler>=3.10; extra == "scheduler"
|
|
74
|
+
Provides-Extra: fastapi
|
|
75
|
+
Requires-Dist: fastapi>=0.100; extra == "fastapi"
|
|
76
|
+
Requires-Dist: httpx>=0.27; extra == "fastapi"
|
|
77
|
+
Provides-Extra: webhooks
|
|
78
|
+
Requires-Dist: httpx>=0.27; extra == "webhooks"
|
|
79
|
+
Provides-Extra: observability
|
|
80
|
+
Requires-Dist: nodus-observability>=0.1.0; extra == "observability"
|
|
81
|
+
Requires-Dist: nodus-observability-framework>=0.1.0; extra == "observability"
|
|
82
|
+
Provides-Extra: extensions
|
|
83
|
+
Requires-Dist: nodus-extension>=0.1.0; extra == "extensions"
|
|
84
|
+
Provides-Extra: full
|
|
85
|
+
Requires-Dist: nodus-sdk[agent,auth,extensions,fastapi,http,llm,memory,observability,redis,scheduler,sql,vector,webhooks,workflow]; extra == "full"
|
|
86
|
+
Provides-Extra: dev
|
|
87
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
88
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
89
|
+
Requires-Dist: respx; extra == "dev"
|
|
90
|
+
Requires-Dist: fastapi[all]; extra == "dev"
|
|
91
|
+
Requires-Dist: sqlalchemy>=2.0; extra == "dev"
|
|
92
|
+
Requires-Dist: apscheduler>=3.10; extra == "dev"
|
|
93
|
+
Requires-Dist: httpx>=0.27; extra == "dev"
|
|
94
|
+
Dynamic: license-file
|
|
95
|
+
|
|
96
|
+
# nodus-sdk
|
|
97
|
+
|
|
98
|
+
**Unified platform SDK for Nodus AI systems.**
|
|
99
|
+
|
|
100
|
+
Single-package installation story for the Nodus ecosystem. Auto-wires
|
|
101
|
+
available packages via `create_runtime(**kwargs)`, provides 9 bridge modules
|
|
102
|
+
for external integrations, and exposes a FastAPI control-plane router.
|
|
103
|
+
|
|
104
|
+
> **Status:** v0.1.0 — prepared, not yet published.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Install
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install nodus-sdk # core only
|
|
112
|
+
pip install "nodus-sdk[agent,sql,fastapi]" # agent + SQLAlchemy + FastAPI
|
|
113
|
+
pip install "nodus-sdk[full]" # everything
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Quick start
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from nodus_sdk import create_runtime
|
|
122
|
+
|
|
123
|
+
rt = create_runtime(memory=True, trace_id="req-001", timeout_ms=None)
|
|
124
|
+
result = rt.run_source('print("hello from sdk")')
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## create_runtime()
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from nodus_sdk import create_runtime, NodusSDKRuntime
|
|
133
|
+
|
|
134
|
+
rt = create_runtime(
|
|
135
|
+
memory=True, # True = auto-configure; or pass a store object
|
|
136
|
+
events=True, # True = auto-configure; or pass EventBusConfig
|
|
137
|
+
extensions=True, # attach ExtensionRegistry
|
|
138
|
+
auth=True, # attach KeyRing
|
|
139
|
+
observability=True, # or pass service name string
|
|
140
|
+
trace_id="tid-001", # injected into every emitted event
|
|
141
|
+
timeout_ms=None, # None = unlimited (required for long-lived services)
|
|
142
|
+
max_steps=None,
|
|
143
|
+
allowed_paths=None,
|
|
144
|
+
project_root=None,
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`create_runtime` returns a `NodusSDKRuntime` — a `NodusRuntime` subclass with
|
|
149
|
+
fluent `attach_*` bridge methods. All capability kwargs accept `True` (default
|
|
150
|
+
config) or a config/store object.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## NodusSDKRuntime fluent API
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from nodus_sdk import NodusSDKRuntime
|
|
158
|
+
from nodus_sdk.bridges.sql import SqlBridge
|
|
159
|
+
from nodus_sdk.bridges.webhook import WebhookBridge
|
|
160
|
+
|
|
161
|
+
rt = (
|
|
162
|
+
NodusSDKRuntime(timeout_ms=None)
|
|
163
|
+
.attach_sql(SqlBridge("postgresql://..."))
|
|
164
|
+
.attach_webhook(WebhookBridge(secret="my-secret"))
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
All `attach_*` methods are idempotent and return `self`.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Bridges
|
|
173
|
+
|
|
174
|
+
| Bridge | Install extra | Key class |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `bridges/redis.py` | `[redis]` | `RedisBridge(url)` → queue_backend, event_bus |
|
|
177
|
+
| `bridges/http.py` | `[http]` | `HttpBridge()` → NodusHttpClient |
|
|
178
|
+
| `bridges/llm.py` | `[llm]` | `LLMBridge(credentials)` → FailoverClient |
|
|
179
|
+
| `bridges/observability.py` | `[observability]` | `init_observability(name, otel=, prometheus=)` |
|
|
180
|
+
| `bridges/sql.py` | `[sql]` | `SqlBridge(url)` → sql_query/sql_execute host fns |
|
|
181
|
+
| `bridges/vector.py` | `[vector]` | `VectorBridge(url, table, dimensions)` → vector_search/upsert/delete |
|
|
182
|
+
| `bridges/scheduler.py` | `[scheduler]` | `SchedulerBridge()` → scheduler_add_interval/cron/cancel |
|
|
183
|
+
| `bridges/webhook.py` | `[webhooks]` | `WebhookBridge(secret=)` → webhook_send |
|
|
184
|
+
| `bridges/api.py` | `[fastapi]` | `create_nodus_router(rt)` + `NodusTraceMiddleware` |
|
|
185
|
+
|
|
186
|
+
**Bridge return type note:** Bridge host functions return Python maps (dicts),
|
|
187
|
+
not Records. Use index access in `.nd` code: `r["status"]`, not `r.status`.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## FastAPI integration
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from fastapi import FastAPI
|
|
195
|
+
from nodus_sdk import create_runtime
|
|
196
|
+
from nodus_sdk.bridges.api import create_nodus_router, NodusTraceMiddleware
|
|
197
|
+
|
|
198
|
+
rt = create_runtime(timeout_ms=None)
|
|
199
|
+
app = FastAPI()
|
|
200
|
+
app.add_middleware(NodusTraceMiddleware, runtime=rt)
|
|
201
|
+
app.include_router(create_nodus_router(rt))
|
|
202
|
+
# Routes: POST /run, GET /health, GET /syscalls, GET|POST|DELETE /memory/{key}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## detect_available()
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from nodus_sdk import detect_available
|
|
211
|
+
|
|
212
|
+
avail = detect_available()
|
|
213
|
+
# {"memory": True, "extension": False, "events": True, ...}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Development
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
pip install -e ".[dev]"
|
|
222
|
+
PYTHONPATH="C:/dev/Coding Language/src" pytest tests/ -q
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Tests require nodus-lang source on `PYTHONPATH`.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# nodus-sdk
|
|
2
|
+
|
|
3
|
+
**Unified platform SDK for Nodus AI systems.**
|
|
4
|
+
|
|
5
|
+
Single-package installation story for the Nodus ecosystem. Auto-wires
|
|
6
|
+
available packages via `create_runtime(**kwargs)`, provides 9 bridge modules
|
|
7
|
+
for external integrations, and exposes a FastAPI control-plane router.
|
|
8
|
+
|
|
9
|
+
> **Status:** v0.1.0 — prepared, not yet published.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install nodus-sdk # core only
|
|
17
|
+
pip install "nodus-sdk[agent,sql,fastapi]" # agent + SQLAlchemy + FastAPI
|
|
18
|
+
pip install "nodus-sdk[full]" # everything
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from nodus_sdk import create_runtime
|
|
27
|
+
|
|
28
|
+
rt = create_runtime(memory=True, trace_id="req-001", timeout_ms=None)
|
|
29
|
+
result = rt.run_source('print("hello from sdk")')
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## create_runtime()
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from nodus_sdk import create_runtime, NodusSDKRuntime
|
|
38
|
+
|
|
39
|
+
rt = create_runtime(
|
|
40
|
+
memory=True, # True = auto-configure; or pass a store object
|
|
41
|
+
events=True, # True = auto-configure; or pass EventBusConfig
|
|
42
|
+
extensions=True, # attach ExtensionRegistry
|
|
43
|
+
auth=True, # attach KeyRing
|
|
44
|
+
observability=True, # or pass service name string
|
|
45
|
+
trace_id="tid-001", # injected into every emitted event
|
|
46
|
+
timeout_ms=None, # None = unlimited (required for long-lived services)
|
|
47
|
+
max_steps=None,
|
|
48
|
+
allowed_paths=None,
|
|
49
|
+
project_root=None,
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`create_runtime` returns a `NodusSDKRuntime` — a `NodusRuntime` subclass with
|
|
54
|
+
fluent `attach_*` bridge methods. All capability kwargs accept `True` (default
|
|
55
|
+
config) or a config/store object.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## NodusSDKRuntime fluent API
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from nodus_sdk import NodusSDKRuntime
|
|
63
|
+
from nodus_sdk.bridges.sql import SqlBridge
|
|
64
|
+
from nodus_sdk.bridges.webhook import WebhookBridge
|
|
65
|
+
|
|
66
|
+
rt = (
|
|
67
|
+
NodusSDKRuntime(timeout_ms=None)
|
|
68
|
+
.attach_sql(SqlBridge("postgresql://..."))
|
|
69
|
+
.attach_webhook(WebhookBridge(secret="my-secret"))
|
|
70
|
+
)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
All `attach_*` methods are idempotent and return `self`.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Bridges
|
|
78
|
+
|
|
79
|
+
| Bridge | Install extra | Key class |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `bridges/redis.py` | `[redis]` | `RedisBridge(url)` → queue_backend, event_bus |
|
|
82
|
+
| `bridges/http.py` | `[http]` | `HttpBridge()` → NodusHttpClient |
|
|
83
|
+
| `bridges/llm.py` | `[llm]` | `LLMBridge(credentials)` → FailoverClient |
|
|
84
|
+
| `bridges/observability.py` | `[observability]` | `init_observability(name, otel=, prometheus=)` |
|
|
85
|
+
| `bridges/sql.py` | `[sql]` | `SqlBridge(url)` → sql_query/sql_execute host fns |
|
|
86
|
+
| `bridges/vector.py` | `[vector]` | `VectorBridge(url, table, dimensions)` → vector_search/upsert/delete |
|
|
87
|
+
| `bridges/scheduler.py` | `[scheduler]` | `SchedulerBridge()` → scheduler_add_interval/cron/cancel |
|
|
88
|
+
| `bridges/webhook.py` | `[webhooks]` | `WebhookBridge(secret=)` → webhook_send |
|
|
89
|
+
| `bridges/api.py` | `[fastapi]` | `create_nodus_router(rt)` + `NodusTraceMiddleware` |
|
|
90
|
+
|
|
91
|
+
**Bridge return type note:** Bridge host functions return Python maps (dicts),
|
|
92
|
+
not Records. Use index access in `.nd` code: `r["status"]`, not `r.status`.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## FastAPI integration
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from fastapi import FastAPI
|
|
100
|
+
from nodus_sdk import create_runtime
|
|
101
|
+
from nodus_sdk.bridges.api import create_nodus_router, NodusTraceMiddleware
|
|
102
|
+
|
|
103
|
+
rt = create_runtime(timeout_ms=None)
|
|
104
|
+
app = FastAPI()
|
|
105
|
+
app.add_middleware(NodusTraceMiddleware, runtime=rt)
|
|
106
|
+
app.include_router(create_nodus_router(rt))
|
|
107
|
+
# Routes: POST /run, GET /health, GET /syscalls, GET|POST|DELETE /memory/{key}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## detect_available()
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from nodus_sdk import detect_available
|
|
116
|
+
|
|
117
|
+
avail = detect_available()
|
|
118
|
+
# {"memory": True, "extension": False, "events": True, ...}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Development
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pip install -e ".[dev]"
|
|
127
|
+
PYTHONPATH="C:/dev/Coding Language/src" pytest tests/ -q
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Tests require nodus-lang source on `PYTHONPATH`.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""nodus-sdk — unified platform SDK for Nodus."""
|
|
2
|
+
|
|
3
|
+
from nodus_sdk._version import __version__
|
|
4
|
+
from nodus_sdk.factory import create_runtime, detect_available
|
|
5
|
+
from nodus_sdk.runtime import NodusSDKRuntime
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"__version__",
|
|
9
|
+
"NodusSDKRuntime",
|
|
10
|
+
"create_runtime",
|
|
11
|
+
"detect_available",
|
|
12
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""nodus_sdk.bridges — Python bridge implementations."""
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""FastAPI bridge — NodusRuntime router and trace middleware."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
7
|
+
|
|
8
|
+
_FASTAPI_AVAILABLE = importlib.util.find_spec("fastapi") is not None
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from nodus.runtime.embedding import NodusRuntime
|
|
12
|
+
|
|
13
|
+
if _FASTAPI_AVAILABLE:
|
|
14
|
+
from pydantic import BaseModel
|
|
15
|
+
|
|
16
|
+
class _RunRequest(BaseModel):
|
|
17
|
+
source: str
|
|
18
|
+
timeout_ms: Optional[int] = None
|
|
19
|
+
|
|
20
|
+
class _MemoryWriteRequest(BaseModel):
|
|
21
|
+
value: Any
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def create_nodus_router(
|
|
25
|
+
runtime: "NodusRuntime",
|
|
26
|
+
*,
|
|
27
|
+
prefix: str = "",
|
|
28
|
+
tags: list[str] | None = None,
|
|
29
|
+
include_memory: bool = True,
|
|
30
|
+
include_syscalls: bool = True,
|
|
31
|
+
) -> Any:
|
|
32
|
+
"""Return a FastAPI APIRouter with Nodus control-plane endpoints.
|
|
33
|
+
|
|
34
|
+
Routes:
|
|
35
|
+
POST {prefix}/run — run Nodus source code
|
|
36
|
+
GET {prefix}/health — runtime health
|
|
37
|
+
GET {prefix}/syscalls — list sys.v1.* syscalls
|
|
38
|
+
GET {prefix}/memory/{key} — read from memory store
|
|
39
|
+
POST {prefix}/memory/{key} — write to memory store
|
|
40
|
+
DELETE{prefix}/memory/{key} — delete from memory store
|
|
41
|
+
|
|
42
|
+
Requires ``nodus-sdk[fastapi]``.
|
|
43
|
+
"""
|
|
44
|
+
if not _FASTAPI_AVAILABLE:
|
|
45
|
+
raise ImportError("fastapi not installed. pip install nodus-sdk[fastapi]")
|
|
46
|
+
|
|
47
|
+
from fastapi import APIRouter
|
|
48
|
+
|
|
49
|
+
from nodus.support.version import __version__ as nodus_version
|
|
50
|
+
|
|
51
|
+
router = APIRouter(prefix=prefix, tags=tags or ["nodus"])
|
|
52
|
+
|
|
53
|
+
@router.post("/run")
|
|
54
|
+
def run_source(req: _RunRequest) -> dict:
|
|
55
|
+
result = runtime.run_source(req.source)
|
|
56
|
+
return {
|
|
57
|
+
"ok": result.get("ok", False),
|
|
58
|
+
"stdout": result.get("stdout", ""),
|
|
59
|
+
"stderr": result.get("stderr", ""),
|
|
60
|
+
"error": result.get("error"),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@router.get("/health")
|
|
64
|
+
def health() -> dict:
|
|
65
|
+
return {"ok": True, "version": nodus_version}
|
|
66
|
+
|
|
67
|
+
if include_syscalls:
|
|
68
|
+
@router.get("/syscalls")
|
|
69
|
+
def list_syscalls() -> list:
|
|
70
|
+
try:
|
|
71
|
+
from nodus.services.syscall_runtime import list_syscalls as _list
|
|
72
|
+
return _list()
|
|
73
|
+
except ImportError:
|
|
74
|
+
return []
|
|
75
|
+
|
|
76
|
+
if include_memory:
|
|
77
|
+
@router.get("/memory/{key}")
|
|
78
|
+
def memory_get(key: str) -> dict:
|
|
79
|
+
from nodus.services.memory_runtime import get_value
|
|
80
|
+
value = get_value(key)
|
|
81
|
+
return {"key": key, "value": value}
|
|
82
|
+
|
|
83
|
+
@router.post("/memory/{key}")
|
|
84
|
+
def memory_set(key: str, req: _MemoryWriteRequest) -> dict:
|
|
85
|
+
from nodus.services.memory_runtime import put_value
|
|
86
|
+
stored = put_value(key, req.value)
|
|
87
|
+
return {"key": key, "value": stored}
|
|
88
|
+
|
|
89
|
+
@router.delete("/memory/{key}")
|
|
90
|
+
def memory_delete(key: str) -> dict:
|
|
91
|
+
from nodus.services.memory_runtime import delete_value
|
|
92
|
+
found = delete_value(key)
|
|
93
|
+
return {"key": key, "found": found}
|
|
94
|
+
|
|
95
|
+
return router
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class NodusTraceMiddleware:
|
|
99
|
+
"""ASGI middleware that injects X-Trace-ID into NodusRuntime before each request.
|
|
100
|
+
|
|
101
|
+
Usage::
|
|
102
|
+
|
|
103
|
+
app = FastAPI()
|
|
104
|
+
rt = create_runtime()
|
|
105
|
+
app.add_middleware(NodusTraceMiddleware, runtime=rt)
|
|
106
|
+
|
|
107
|
+
Requires ``nodus-sdk[fastapi]``.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
def __init__(self, app: Any, *, runtime: "NodusRuntime", header: str = "X-Trace-ID") -> None:
|
|
111
|
+
self.app = app
|
|
112
|
+
self.runtime = runtime
|
|
113
|
+
self.header = header.lower().encode()
|
|
114
|
+
|
|
115
|
+
async def __call__(self, scope: dict, receive: Any, send: Any) -> None:
|
|
116
|
+
if scope["type"] == "http":
|
|
117
|
+
trace_id = None
|
|
118
|
+
for name, value in scope.get("headers", []):
|
|
119
|
+
if name.lower() == self.header:
|
|
120
|
+
trace_id = value.decode()
|
|
121
|
+
break
|
|
122
|
+
if trace_id:
|
|
123
|
+
self.runtime.set_trace_id(trace_id)
|
|
124
|
+
await self.app(scope, receive, send)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""HttpBridge — thin bridge over nodus-http."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
_AVAILABLE = importlib.util.find_spec("nodus_http") is not None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HttpBridge:
|
|
12
|
+
"""Thin bridge wrapping nodus-http's HttpClient with optional circuit-breaker.
|
|
13
|
+
|
|
14
|
+
Requires ``nodus-sdk[http]`` (``nodus-http``).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
base_url: str | None = None,
|
|
20
|
+
timeout: float = 30.0,
|
|
21
|
+
circuit_breaker: Any = None,
|
|
22
|
+
retry_config: Any = None,
|
|
23
|
+
) -> None:
|
|
24
|
+
self._base_url = base_url
|
|
25
|
+
self._timeout = timeout
|
|
26
|
+
self._circuit_breaker = circuit_breaker
|
|
27
|
+
self._retry_config = retry_config
|
|
28
|
+
|
|
29
|
+
def available(self) -> bool:
|
|
30
|
+
return _AVAILABLE
|
|
31
|
+
|
|
32
|
+
def client(self) -> Any:
|
|
33
|
+
"""Return a configured NodusHttpClient."""
|
|
34
|
+
if not _AVAILABLE:
|
|
35
|
+
raise ImportError("nodus-http not installed. pip install nodus-sdk[http]")
|
|
36
|
+
from nodus_http import NodusHttpClient
|
|
37
|
+
kwargs: dict[str, Any] = {}
|
|
38
|
+
if self._base_url:
|
|
39
|
+
kwargs["base_url"] = self._base_url
|
|
40
|
+
if self._circuit_breaker is not None:
|
|
41
|
+
kwargs["circuit_breaker"] = self._circuit_breaker
|
|
42
|
+
return NodusHttpClient(**kwargs)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""LLMBridge — thin bridge over nodus-llm's FailoverClient."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
_AVAILABLE = importlib.util.find_spec("nodus_llm") is not None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LLMBridge:
|
|
12
|
+
"""Thin bridge wrapping nodus-llm for multi-provider LLM failover.
|
|
13
|
+
|
|
14
|
+
Requires ``nodus-sdk[llm]`` (``nodus-llm``).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, credentials: list[Any] | None = None) -> None:
|
|
18
|
+
self._credentials = credentials or []
|
|
19
|
+
|
|
20
|
+
def available(self) -> bool:
|
|
21
|
+
return _AVAILABLE
|
|
22
|
+
|
|
23
|
+
def failover_client(self, provider_fn: Any = None) -> Any:
|
|
24
|
+
"""Return a FailoverClient configured with provided credentials.
|
|
25
|
+
|
|
26
|
+
provider_fn maps CredentialProfile → LLMClient. Required by nodus-llm;
|
|
27
|
+
pass a callable that constructs a provider-specific client.
|
|
28
|
+
"""
|
|
29
|
+
if not _AVAILABLE:
|
|
30
|
+
raise ImportError("nodus-llm not installed. pip install nodus-sdk[llm]")
|
|
31
|
+
if provider_fn is None:
|
|
32
|
+
raise ValueError(
|
|
33
|
+
"provider_fn is required: pass a callable that maps CredentialProfile → LLMClient"
|
|
34
|
+
)
|
|
35
|
+
from nodus_llm import FailoverClient, CredentialStore
|
|
36
|
+
store = CredentialStore(profiles=self._credentials)
|
|
37
|
+
return FailoverClient(store, provider_fn)
|
|
38
|
+
|
|
39
|
+
def credential_store(self) -> Any:
|
|
40
|
+
"""Return a CredentialStore from the provided credential profiles."""
|
|
41
|
+
if not _AVAILABLE:
|
|
42
|
+
raise ImportError("nodus-llm not installed. pip install nodus-sdk[llm]")
|
|
43
|
+
from nodus_llm import CredentialStore
|
|
44
|
+
return CredentialStore(profiles=self._credentials)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Observability bridge — thin wrapper over nodus-observability bootstrap."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
|
|
7
|
+
_AVAILABLE = importlib.util.find_spec("nodus_observability") is not None
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def available() -> bool:
|
|
11
|
+
return _AVAILABLE
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def init_observability(
|
|
15
|
+
service_name: str,
|
|
16
|
+
*,
|
|
17
|
+
otel: bool = False,
|
|
18
|
+
prometheus: bool = False,
|
|
19
|
+
configure_logging: bool = True,
|
|
20
|
+
) -> None:
|
|
21
|
+
"""Bootstrap observability for the given service.
|
|
22
|
+
|
|
23
|
+
Calls nodus-observability's init_otel() and/or create_registry() depending
|
|
24
|
+
on the flags. Safe to call multiple times (subsequent calls are no-ops at
|
|
25
|
+
the nodus-observability layer).
|
|
26
|
+
|
|
27
|
+
Requires ``nodus-sdk[observability]``.
|
|
28
|
+
"""
|
|
29
|
+
if not _AVAILABLE:
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
if configure_logging:
|
|
33
|
+
try:
|
|
34
|
+
from nodus_observability import configure_logging as _conf_logging
|
|
35
|
+
_conf_logging()
|
|
36
|
+
except (ImportError, TypeError):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
if otel:
|
|
40
|
+
try:
|
|
41
|
+
from nodus_observability import init_otel
|
|
42
|
+
init_otel(service_name)
|
|
43
|
+
except (ImportError, TypeError):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
if prometheus:
|
|
47
|
+
try:
|
|
48
|
+
from nodus_observability import create_registry
|
|
49
|
+
create_registry()
|
|
50
|
+
except (ImportError, TypeError):
|
|
51
|
+
pass
|