holokai-neuron-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.
- holokai_neuron_sdk-0.1.0/.gitignore +9 -0
- holokai_neuron_sdk-0.1.0/LICENSE +21 -0
- holokai_neuron_sdk-0.1.0/PKG-INFO +210 -0
- holokai_neuron_sdk-0.1.0/README.md +178 -0
- holokai_neuron_sdk-0.1.0/examples/http_fetch/README.md +74 -0
- holokai_neuron_sdk-0.1.0/examples/web_search/README.md +80 -0
- holokai_neuron_sdk-0.1.0/pyproject.toml +73 -0
- holokai_neuron_sdk-0.1.0/src/holokai_neuron_sdk/__init__.py +129 -0
- holokai_neuron_sdk-0.1.0/src/holokai_neuron_sdk/_heartbeat.py +101 -0
- holokai_neuron_sdk-0.1.0/src/holokai_neuron_sdk/_lease_runner.py +270 -0
- holokai_neuron_sdk-0.1.0/src/holokai_neuron_sdk/neuron.py +491 -0
- holokai_neuron_sdk-0.1.0/src/holokai_neuron_sdk/protocol.py +312 -0
- holokai_neuron_sdk-0.1.0/src/holokai_neuron_sdk/sse.py +94 -0
- holokai_neuron_sdk-0.1.0/src/holokai_neuron_sdk/transport.py +371 -0
- holokai_neuron_sdk-0.1.0/tests/__init__.py +0 -0
- holokai_neuron_sdk-0.1.0/tests/e2e/__init__.py +0 -0
- holokai_neuron_sdk-0.1.0/tests/e2e/fake_gateway.py +190 -0
- holokai_neuron_sdk-0.1.0/tests/e2e/test_web_search_e2e.py +334 -0
- holokai_neuron_sdk-0.1.0/tests/test_example_http_fetch.py +216 -0
- holokai_neuron_sdk-0.1.0/tests/test_example_web_search.py +248 -0
- holokai_neuron_sdk-0.1.0/tests/test_heartbeat.py +103 -0
- holokai_neuron_sdk-0.1.0/tests/test_lease_runner.py +272 -0
- holokai_neuron_sdk-0.1.0/tests/test_neuron.py +327 -0
- holokai_neuron_sdk-0.1.0/tests/test_protocol.py +228 -0
- holokai_neuron_sdk-0.1.0/tests/test_sse.py +103 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Holokai
|
|
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,210 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: holokai-neuron-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SDK for building Holokai neurons in Python — connect to a BigBrain gateway over HTTP+SSE and execute capability-typed tasks.
|
|
5
|
+
Project-URL: Homepage, https://github.com/holok-ai/bigbrain/tree/main/packages/neuron-sdk-python#readme
|
|
6
|
+
Project-URL: Repository, https://github.com/holok-ai/bigbrain
|
|
7
|
+
Project-URL: Issues, https://github.com/holok-ai/bigbrain/issues
|
|
8
|
+
Author: Holokai
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent,bigbrain,holokai,neuron,sdk,sse,workflow
|
|
12
|
+
Classifier: Framework :: AsyncIO
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: httpx<1,>=0.27
|
|
22
|
+
Requires-Dist: jsonschema<5,>=4.21
|
|
23
|
+
Requires-Dist: pydantic<3,>=2.6
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: aiohttp>=3.9; extra == 'dev'
|
|
26
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
29
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# holokai-neuron-sdk
|
|
34
|
+
|
|
35
|
+
SDK for building **Holokai neurons** in Python — connect to a BigBrain
|
|
36
|
+
gateway over HTTP+SSE and execute capability-typed tasks.
|
|
37
|
+
|
|
38
|
+
This is the Python sibling of [`@holokai/neuron-sdk`][ts-sdk] (TypeScript)
|
|
39
|
+
and speaks the same wire protocol byte-for-byte
|
|
40
|
+
(`packages/neuron-protocol`).
|
|
41
|
+
|
|
42
|
+
[ts-sdk]: ../neuron-sdk
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install holokai-neuron-sdk
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Requires Python 3.10+.
|
|
51
|
+
|
|
52
|
+
## Quick start
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import asyncio
|
|
56
|
+
import logging
|
|
57
|
+
|
|
58
|
+
from holokai_neuron_sdk import Capability, HandlerContext, Neuron
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def echo(input, ctx: HandlerContext):
|
|
62
|
+
await ctx.progress(percent=50, message="halfway")
|
|
63
|
+
return {"echo": input}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
async def main():
|
|
67
|
+
neuron = Neuron(
|
|
68
|
+
gateway_url="https://api.holokai.dev",
|
|
69
|
+
neuron_id="my-neuron-1",
|
|
70
|
+
auth=lambda: "<bearer token>", # sync or async; called per request
|
|
71
|
+
logger=logging.getLogger("neuron"),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
neuron.register_handler(
|
|
75
|
+
Capability(type="example/echo", scope="any", concurrency=4),
|
|
76
|
+
echo,
|
|
77
|
+
input_schema={"type": "object"},
|
|
78
|
+
output_schema={
|
|
79
|
+
"type": "object",
|
|
80
|
+
"properties": {"echo": {}},
|
|
81
|
+
"required": ["echo"],
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
await neuron.start()
|
|
86
|
+
try:
|
|
87
|
+
await asyncio.Event().wait() # run until cancelled
|
|
88
|
+
finally:
|
|
89
|
+
await neuron.stop(drain=True, timeout=30)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
asyncio.run(main())
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Concepts
|
|
96
|
+
|
|
97
|
+
| Concept | What it is |
|
|
98
|
+
| ------- | ---------- |
|
|
99
|
+
| **Neuron** | A process that registers itself with a BigBrain gateway and offers one or more capabilities. |
|
|
100
|
+
| **Capability** | A namespaced task type (`owner/package[/name][.variant]`) with a scope (`"any"` or `{onBehalfOf: userId}`) and a per-capability concurrency. |
|
|
101
|
+
| **Lease** | A single in-flight task delivery. Carries the input, schemas, and a deadline. The neuron acks (success), nacks (failure), or progresses. |
|
|
102
|
+
| **Cancel** | The gateway can revoke a lease mid-flight. The handler's cancellation `asyncio.Event` is set, the task is cancelled, and a `cancelled` nack is sent. |
|
|
103
|
+
| **Heartbeat** | The SDK posts a heartbeat every 10s with the current in-flight lease list. The gateway uses it to detect dead neurons and to renew lease TTLs for long-running tasks. |
|
|
104
|
+
|
|
105
|
+
## Wire protocol
|
|
106
|
+
|
|
107
|
+
* **Transport**: SSE (server → neuron) + HTTPS POST (neuron → server).
|
|
108
|
+
* **Endpoints**:
|
|
109
|
+
* `GET /neuron/events?neuronId=…` — SSE stream
|
|
110
|
+
* `POST /neuron/{register, update-capability, heartbeat, ack, nack, progress}`
|
|
111
|
+
* **Authentication**: `Authorization: Bearer <jwt>`. The `auth` callback is
|
|
112
|
+
invoked on every POST and SSE open, and is given one chance to refresh on
|
|
113
|
+
401 before the SDK escalates to reconnect-with-backoff.
|
|
114
|
+
* **Schemas**: input/output JSON Schema arrives on the lease frame; the SDK
|
|
115
|
+
validates with `jsonschema` and emits a `schema-mismatch` nack on failure.
|
|
116
|
+
* **Versioning**: `PROTOCOL_VERSION = "1.0.0"` (free-form on the wire; the
|
|
117
|
+
gateway negotiates compatibility).
|
|
118
|
+
|
|
119
|
+
## Handler contract
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
async def handler(input, ctx: HandlerContext):
|
|
123
|
+
# ctx.task — the full task payload
|
|
124
|
+
# ctx.lease_id — the lease the gateway holds for this delivery
|
|
125
|
+
# ctx.cancelled — asyncio.Event set on cancel / shutdown
|
|
126
|
+
# ctx.log — logger scoped to this lease
|
|
127
|
+
# ctx.progress(...) — non-blocking progress update
|
|
128
|
+
# ctx.fail(reason, code=...) — terminal nack (do not catch)
|
|
129
|
+
return {...}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
* **Sync handlers** are supported; the runner awaits awaitable return values
|
|
133
|
+
and otherwise treats the return as the result.
|
|
134
|
+
* **Cancellation**: handlers wrapping I/O should cooperate with
|
|
135
|
+
`ctx.cancelled` (e.g. `asyncio.wait` with `[ctx.cancelled.wait(), …]`).
|
|
136
|
+
* **Idempotency** is the handler author's responsibility — the framework
|
|
137
|
+
does not checkpoint mid-task.
|
|
138
|
+
|
|
139
|
+
## Failure classification
|
|
140
|
+
|
|
141
|
+
| Outcome | Wire kind |
|
|
142
|
+
|--------------------------------------|--------------------|
|
|
143
|
+
| Handler returns | `ack` |
|
|
144
|
+
| Handler raises `asyncio.CancelledError` (or cancel event set) | `nack: cancelled` |
|
|
145
|
+
| `ctx.fail(...)` | `nack: terminal` |
|
|
146
|
+
| Handler raises any other exception | `nack: retryable` |
|
|
147
|
+
| Input/output fails JSON Schema | `nack: schema-mismatch` |
|
|
148
|
+
|
|
149
|
+
Retry policy lives on the workflow (server-side), never on the SDK.
|
|
150
|
+
|
|
151
|
+
## Architecture
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
┌────────────┐ POST register / heartbeat / ack / nack / progress
|
|
155
|
+
│ Neuron │──────────────────────────────────────────────────────▶│
|
|
156
|
+
│ (this │ │ BigBrain
|
|
157
|
+
│ SDK) │◀──────────────────────────── SSE: lease / cancel ────│ gateway
|
|
158
|
+
└────────────┘
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
* `HttpTransport` owns one SSE stream and a shared `httpx.AsyncClient`.
|
|
162
|
+
* `Neuron` wires the transport to a heartbeat loop and a per-capability
|
|
163
|
+
semaphore-bounded dispatcher.
|
|
164
|
+
* `run_lease(...)` validates input, invokes the handler, validates output,
|
|
165
|
+
and posts the right ack/nack frame.
|
|
166
|
+
|
|
167
|
+
## Examples
|
|
168
|
+
|
|
169
|
+
| Example | Capability | What it does |
|
|
170
|
+
| ------- | ---------- | ------------ |
|
|
171
|
+
| [`examples/http_fetch/`](examples/http_fetch/) | `examples/http.fetch` | Fetches an arbitrary HTTP(S) URL via `httpx` and returns status/headers/body. |
|
|
172
|
+
| [`examples/web_search/`](examples/web_search/) | `examples/web.search` | Keyless web search via DuckDuckGo HTML scrape. No API key, but inherently brittle — see the example README for caveats. |
|
|
173
|
+
|
|
174
|
+
Each ships with input/output JSON Schemas, cooperative cancellation
|
|
175
|
+
(`ctx.cancelled`), and a CLI runner. Run them the same way:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
pip install -e .
|
|
179
|
+
BIGBRAIN_GATEWAY_URL=https://api.holokai.dev \
|
|
180
|
+
BIGBRAIN_NEURON_ID=my-python-neuron-1 \
|
|
181
|
+
BIGBRAIN_TOKEN=eyJ... \
|
|
182
|
+
python -m examples.http_fetch # or examples.web_search
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Custom transports
|
|
186
|
+
|
|
187
|
+
`HttpTransport` is the default but the public `Transport` shape is documented
|
|
188
|
+
on `TransportCallbacks` and `Neuron(transport=…)` accepts any object that
|
|
189
|
+
matches the protocol — handy for tests or in-process embeddings.
|
|
190
|
+
|
|
191
|
+
## Tests
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Unit + deterministic e2e (default — no network).
|
|
195
|
+
pytest
|
|
196
|
+
|
|
197
|
+
# Network-gated e2e: hits real DuckDuckGo through the real SDK to catch
|
|
198
|
+
# scraper drift. Skipped unless explicitly enabled.
|
|
199
|
+
E2E_NETWORK=1 pytest -m e2e_network
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The e2e tests stand up a tiny aiohttp server on loopback that serves both
|
|
203
|
+
the BigBrain `/neuron/*` surface and a stub for DuckDuckGo, then run the
|
|
204
|
+
real `Neuron` SDK against it. Every byte flows over real HTTP — SSE in,
|
|
205
|
+
POSTs out — exercising the full `register → lease → handler → ack` loop.
|
|
206
|
+
See [`tests/e2e/`](tests/e2e/).
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# holokai-neuron-sdk
|
|
2
|
+
|
|
3
|
+
SDK for building **Holokai neurons** in Python — connect to a BigBrain
|
|
4
|
+
gateway over HTTP+SSE and execute capability-typed tasks.
|
|
5
|
+
|
|
6
|
+
This is the Python sibling of [`@holokai/neuron-sdk`][ts-sdk] (TypeScript)
|
|
7
|
+
and speaks the same wire protocol byte-for-byte
|
|
8
|
+
(`packages/neuron-protocol`).
|
|
9
|
+
|
|
10
|
+
[ts-sdk]: ../neuron-sdk
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install holokai-neuron-sdk
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Requires Python 3.10+.
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
import asyncio
|
|
24
|
+
import logging
|
|
25
|
+
|
|
26
|
+
from holokai_neuron_sdk import Capability, HandlerContext, Neuron
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def echo(input, ctx: HandlerContext):
|
|
30
|
+
await ctx.progress(percent=50, message="halfway")
|
|
31
|
+
return {"echo": input}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def main():
|
|
35
|
+
neuron = Neuron(
|
|
36
|
+
gateway_url="https://api.holokai.dev",
|
|
37
|
+
neuron_id="my-neuron-1",
|
|
38
|
+
auth=lambda: "<bearer token>", # sync or async; called per request
|
|
39
|
+
logger=logging.getLogger("neuron"),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
neuron.register_handler(
|
|
43
|
+
Capability(type="example/echo", scope="any", concurrency=4),
|
|
44
|
+
echo,
|
|
45
|
+
input_schema={"type": "object"},
|
|
46
|
+
output_schema={
|
|
47
|
+
"type": "object",
|
|
48
|
+
"properties": {"echo": {}},
|
|
49
|
+
"required": ["echo"],
|
|
50
|
+
},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
await neuron.start()
|
|
54
|
+
try:
|
|
55
|
+
await asyncio.Event().wait() # run until cancelled
|
|
56
|
+
finally:
|
|
57
|
+
await neuron.stop(drain=True, timeout=30)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
asyncio.run(main())
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Concepts
|
|
64
|
+
|
|
65
|
+
| Concept | What it is |
|
|
66
|
+
| ------- | ---------- |
|
|
67
|
+
| **Neuron** | A process that registers itself with a BigBrain gateway and offers one or more capabilities. |
|
|
68
|
+
| **Capability** | A namespaced task type (`owner/package[/name][.variant]`) with a scope (`"any"` or `{onBehalfOf: userId}`) and a per-capability concurrency. |
|
|
69
|
+
| **Lease** | A single in-flight task delivery. Carries the input, schemas, and a deadline. The neuron acks (success), nacks (failure), or progresses. |
|
|
70
|
+
| **Cancel** | The gateway can revoke a lease mid-flight. The handler's cancellation `asyncio.Event` is set, the task is cancelled, and a `cancelled` nack is sent. |
|
|
71
|
+
| **Heartbeat** | The SDK posts a heartbeat every 10s with the current in-flight lease list. The gateway uses it to detect dead neurons and to renew lease TTLs for long-running tasks. |
|
|
72
|
+
|
|
73
|
+
## Wire protocol
|
|
74
|
+
|
|
75
|
+
* **Transport**: SSE (server → neuron) + HTTPS POST (neuron → server).
|
|
76
|
+
* **Endpoints**:
|
|
77
|
+
* `GET /neuron/events?neuronId=…` — SSE stream
|
|
78
|
+
* `POST /neuron/{register, update-capability, heartbeat, ack, nack, progress}`
|
|
79
|
+
* **Authentication**: `Authorization: Bearer <jwt>`. The `auth` callback is
|
|
80
|
+
invoked on every POST and SSE open, and is given one chance to refresh on
|
|
81
|
+
401 before the SDK escalates to reconnect-with-backoff.
|
|
82
|
+
* **Schemas**: input/output JSON Schema arrives on the lease frame; the SDK
|
|
83
|
+
validates with `jsonschema` and emits a `schema-mismatch` nack on failure.
|
|
84
|
+
* **Versioning**: `PROTOCOL_VERSION = "1.0.0"` (free-form on the wire; the
|
|
85
|
+
gateway negotiates compatibility).
|
|
86
|
+
|
|
87
|
+
## Handler contract
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
async def handler(input, ctx: HandlerContext):
|
|
91
|
+
# ctx.task — the full task payload
|
|
92
|
+
# ctx.lease_id — the lease the gateway holds for this delivery
|
|
93
|
+
# ctx.cancelled — asyncio.Event set on cancel / shutdown
|
|
94
|
+
# ctx.log — logger scoped to this lease
|
|
95
|
+
# ctx.progress(...) — non-blocking progress update
|
|
96
|
+
# ctx.fail(reason, code=...) — terminal nack (do not catch)
|
|
97
|
+
return {...}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
* **Sync handlers** are supported; the runner awaits awaitable return values
|
|
101
|
+
and otherwise treats the return as the result.
|
|
102
|
+
* **Cancellation**: handlers wrapping I/O should cooperate with
|
|
103
|
+
`ctx.cancelled` (e.g. `asyncio.wait` with `[ctx.cancelled.wait(), …]`).
|
|
104
|
+
* **Idempotency** is the handler author's responsibility — the framework
|
|
105
|
+
does not checkpoint mid-task.
|
|
106
|
+
|
|
107
|
+
## Failure classification
|
|
108
|
+
|
|
109
|
+
| Outcome | Wire kind |
|
|
110
|
+
|--------------------------------------|--------------------|
|
|
111
|
+
| Handler returns | `ack` |
|
|
112
|
+
| Handler raises `asyncio.CancelledError` (or cancel event set) | `nack: cancelled` |
|
|
113
|
+
| `ctx.fail(...)` | `nack: terminal` |
|
|
114
|
+
| Handler raises any other exception | `nack: retryable` |
|
|
115
|
+
| Input/output fails JSON Schema | `nack: schema-mismatch` |
|
|
116
|
+
|
|
117
|
+
Retry policy lives on the workflow (server-side), never on the SDK.
|
|
118
|
+
|
|
119
|
+
## Architecture
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
┌────────────┐ POST register / heartbeat / ack / nack / progress
|
|
123
|
+
│ Neuron │──────────────────────────────────────────────────────▶│
|
|
124
|
+
│ (this │ │ BigBrain
|
|
125
|
+
│ SDK) │◀──────────────────────────── SSE: lease / cancel ────│ gateway
|
|
126
|
+
└────────────┘
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
* `HttpTransport` owns one SSE stream and a shared `httpx.AsyncClient`.
|
|
130
|
+
* `Neuron` wires the transport to a heartbeat loop and a per-capability
|
|
131
|
+
semaphore-bounded dispatcher.
|
|
132
|
+
* `run_lease(...)` validates input, invokes the handler, validates output,
|
|
133
|
+
and posts the right ack/nack frame.
|
|
134
|
+
|
|
135
|
+
## Examples
|
|
136
|
+
|
|
137
|
+
| Example | Capability | What it does |
|
|
138
|
+
| ------- | ---------- | ------------ |
|
|
139
|
+
| [`examples/http_fetch/`](examples/http_fetch/) | `examples/http.fetch` | Fetches an arbitrary HTTP(S) URL via `httpx` and returns status/headers/body. |
|
|
140
|
+
| [`examples/web_search/`](examples/web_search/) | `examples/web.search` | Keyless web search via DuckDuckGo HTML scrape. No API key, but inherently brittle — see the example README for caveats. |
|
|
141
|
+
|
|
142
|
+
Each ships with input/output JSON Schemas, cooperative cancellation
|
|
143
|
+
(`ctx.cancelled`), and a CLI runner. Run them the same way:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install -e .
|
|
147
|
+
BIGBRAIN_GATEWAY_URL=https://api.holokai.dev \
|
|
148
|
+
BIGBRAIN_NEURON_ID=my-python-neuron-1 \
|
|
149
|
+
BIGBRAIN_TOKEN=eyJ... \
|
|
150
|
+
python -m examples.http_fetch # or examples.web_search
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Custom transports
|
|
154
|
+
|
|
155
|
+
`HttpTransport` is the default but the public `Transport` shape is documented
|
|
156
|
+
on `TransportCallbacks` and `Neuron(transport=…)` accepts any object that
|
|
157
|
+
matches the protocol — handy for tests or in-process embeddings.
|
|
158
|
+
|
|
159
|
+
## Tests
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# Unit + deterministic e2e (default — no network).
|
|
163
|
+
pytest
|
|
164
|
+
|
|
165
|
+
# Network-gated e2e: hits real DuckDuckGo through the real SDK to catch
|
|
166
|
+
# scraper drift. Skipped unless explicitly enabled.
|
|
167
|
+
E2E_NETWORK=1 pytest -m e2e_network
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The e2e tests stand up a tiny aiohttp server on loopback that serves both
|
|
171
|
+
the BigBrain `/neuron/*` surface and a stub for DuckDuckGo, then run the
|
|
172
|
+
real `Neuron` SDK against it. Every byte flows over real HTTP — SSE in,
|
|
173
|
+
POSTs out — exercising the full `register → lease → handler → ack` loop.
|
|
174
|
+
See [`tests/e2e/`](tests/e2e/).
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# `examples/http.fetch` — runnable Python neuron
|
|
2
|
+
|
|
3
|
+
Generic outbound HTTP fetcher exposed as a BigBrain capability. A workflow
|
|
4
|
+
plan that resolves to `examples/http.fetch` will arrive at this neuron as a
|
|
5
|
+
lease, run an `httpx` request, and ack with the response.
|
|
6
|
+
|
|
7
|
+
## Run
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cd packages/neuron-sdk-python
|
|
11
|
+
pip install -e .
|
|
12
|
+
BIGBRAIN_GATEWAY_URL=https://api.holokai.dev \
|
|
13
|
+
BIGBRAIN_NEURON_ID=my-python-neuron-1 \
|
|
14
|
+
BIGBRAIN_TOKEN=eyJhbGciOi... \
|
|
15
|
+
python -m examples.http_fetch
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`BIGBRAIN_TOKEN_FILE=/path/to/token` is supported as an alternative — the
|
|
19
|
+
file is re-read on every auth refresh, so you can rotate the token without
|
|
20
|
+
restarting.
|
|
21
|
+
|
|
22
|
+
## Capability shape
|
|
23
|
+
|
|
24
|
+
| Field | Value |
|
|
25
|
+
| ----- | ----- |
|
|
26
|
+
| `type` | `examples/http.fetch` |
|
|
27
|
+
| `scope` | `any` |
|
|
28
|
+
| `concurrency` | 8 (handle up to 8 leases in parallel per neuron) |
|
|
29
|
+
| `leaseTtlMs` | 60_000 (longer than the default to cover slow upstreams) |
|
|
30
|
+
|
|
31
|
+
### Input
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"url": "https://example.com/api/thing",
|
|
36
|
+
"method": "GET",
|
|
37
|
+
"headers": {"User-Agent": "bigbrain-neuron"},
|
|
38
|
+
"body": null,
|
|
39
|
+
"timeout_ms": 10000,
|
|
40
|
+
"max_response_bytes": 1000000,
|
|
41
|
+
"follow_redirects": true
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Only `url` is required.
|
|
46
|
+
|
|
47
|
+
### Output
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"status": 200,
|
|
52
|
+
"headers": {"content-type": "application/json", "...": "..."},
|
|
53
|
+
"body": "{\"hello\":\"world\"}",
|
|
54
|
+
"truncated": false,
|
|
55
|
+
"duration_ms": 87,
|
|
56
|
+
"final_url": "https://example.com/api/thing"
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
* HTTP non-2xx responses are returned as-is — the workflow author decides
|
|
61
|
+
whether a 4xx/5xx is fatal.
|
|
62
|
+
* Response bodies are decoded as UTF-8 (replacement on errors) and clipped
|
|
63
|
+
to `max_response_bytes`. `truncated: true` means there was more.
|
|
64
|
+
* Network errors (DNS, connect refused, timeout) propagate as a
|
|
65
|
+
`retryable` nack — the workflow's retry policy decides what's next.
|
|
66
|
+
* If the gateway sends a `cancel` frame mid-request the in-flight `httpx`
|
|
67
|
+
call is aborted and the lease is nacked as `cancelled`.
|
|
68
|
+
|
|
69
|
+
## Files
|
|
70
|
+
|
|
71
|
+
| File | What |
|
|
72
|
+
| ---- | ---- |
|
|
73
|
+
| [`capability.py`](capability.py) | Handler, JSON schemas, `Capability` definition. Importable on its own. |
|
|
74
|
+
| [`__main__.py`](__main__.py) | CLI runner. Reads gateway URL / neuron id / token from env, registers the handler, runs until SIGINT. |
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# `examples/web.search` — keyless web search
|
|
2
|
+
|
|
3
|
+
Exposes web search to BigBrain workflows by scraping DuckDuckGo's HTML
|
|
4
|
+
endpoint (`https://html.duckduckgo.com/html/`). No API key, no extra
|
|
5
|
+
dependencies beyond `httpx` and stdlib `html.parser`.
|
|
6
|
+
|
|
7
|
+
## ⚠️ Caveats
|
|
8
|
+
|
|
9
|
+
* **Brittle by design** — DuckDuckGo can change the HTML markup or
|
|
10
|
+
tighten rate-limits at any time. If results stop coming back, look at
|
|
11
|
+
the response (`LOG_LEVEL=DEBUG`) and adjust the parser, or swap in
|
|
12
|
+
another provider.
|
|
13
|
+
* **Rate-limited** — DDG returns HTTP 202 (with no results) when it
|
|
14
|
+
decides you're hitting too hard. The handler surfaces that as a
|
|
15
|
+
`terminal` nack so the workflow author chooses a back-off rather than
|
|
16
|
+
the framework retrying into a tighter ban.
|
|
17
|
+
* **No personalization / safe-search controls** — kept intentionally
|
|
18
|
+
thin. Wire those in via the `region` field (passed through as
|
|
19
|
+
DuckDuckGo's `kl=` parameter) or replace the handler.
|
|
20
|
+
|
|
21
|
+
## Run
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd packages/neuron-sdk-python
|
|
25
|
+
pip install -e .
|
|
26
|
+
BIGBRAIN_GATEWAY_URL=https://api.holokai.dev \
|
|
27
|
+
BIGBRAIN_NEURON_ID=my-python-neuron-1 \
|
|
28
|
+
BIGBRAIN_TOKEN=eyJ... \
|
|
29
|
+
python -m examples.web_search
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Optional overrides:
|
|
33
|
+
|
|
34
|
+
| Env var | Default | What it does |
|
|
35
|
+
| ------- | ------- | ------------ |
|
|
36
|
+
| `EXAMPLES_WEB_SEARCH_ENDPOINT` | `https://html.duckduckgo.com/html/` | Swap providers (e.g. a private SearXNG instance — though parser will need to be adjusted to the new HTML shape). |
|
|
37
|
+
| `EXAMPLES_WEB_SEARCH_UA` | a desktop Chrome UA | Set the `User-Agent`. DDG returns empty results for the stock httpx UA. |
|
|
38
|
+
|
|
39
|
+
## Capability shape
|
|
40
|
+
|
|
41
|
+
| Field | Value |
|
|
42
|
+
| ----- | ----- |
|
|
43
|
+
| `type` | `examples/web.search` |
|
|
44
|
+
| `scope` | `any` |
|
|
45
|
+
| `concurrency` | 4 |
|
|
46
|
+
| `leaseTtlMs` | 30_000 |
|
|
47
|
+
|
|
48
|
+
### Input
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"query": "neuron protocol holokai",
|
|
53
|
+
"max_results": 10,
|
|
54
|
+
"region": "wt-wt"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Only `query` is required.
|
|
59
|
+
|
|
60
|
+
### Output
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"query": "neuron protocol holokai",
|
|
65
|
+
"results": [
|
|
66
|
+
{
|
|
67
|
+
"title": "Holokai BigBrain — Neuron architecture",
|
|
68
|
+
"url": "https://example.test/neuron",
|
|
69
|
+
"snippet": "Lease-based capability dispatch over SSE+POST..."
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Files
|
|
76
|
+
|
|
77
|
+
| File | What |
|
|
78
|
+
| ---- | ---- |
|
|
79
|
+
| [`capability.py`](capability.py) | Handler, JSON Schemas, `Capability` definition, defensive HTML parser. |
|
|
80
|
+
| [`__main__.py`](__main__.py) | CLI runner. Reads gateway URL / neuron id / token from env, registers the handler, runs until SIGINT. |
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.24"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "holokai-neuron-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "SDK for building Holokai neurons in Python — connect to a BigBrain gateway over HTTP+SSE and execute capability-typed tasks."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Holokai" }]
|
|
13
|
+
keywords = ["holokai", "bigbrain", "neuron", "sdk", "workflow", "sse", "agent"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Framework :: AsyncIO",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Topic :: Software Development :: Libraries",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"httpx>=0.27,<1",
|
|
26
|
+
"pydantic>=2.6,<3",
|
|
27
|
+
"jsonschema>=4.21,<5",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://github.com/holok-ai/bigbrain/tree/main/packages/neuron-sdk-python#readme"
|
|
32
|
+
Repository = "https://github.com/holok-ai/bigbrain"
|
|
33
|
+
Issues = "https://github.com/holok-ai/bigbrain/issues"
|
|
34
|
+
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=8",
|
|
38
|
+
"pytest-asyncio>=0.23",
|
|
39
|
+
"respx>=0.21",
|
|
40
|
+
"aiohttp>=3.9",
|
|
41
|
+
"mypy>=1.10",
|
|
42
|
+
"ruff>=0.5",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.wheel]
|
|
46
|
+
packages = ["src/holokai_neuron_sdk"]
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.sdist]
|
|
49
|
+
include = ["src/holokai_neuron_sdk", "README.md", "LICENSE", "tests"]
|
|
50
|
+
|
|
51
|
+
[tool.pytest.ini_options]
|
|
52
|
+
testpaths = ["tests"]
|
|
53
|
+
asyncio_mode = "auto"
|
|
54
|
+
addopts = "-ra"
|
|
55
|
+
pythonpath = ["."]
|
|
56
|
+
markers = [
|
|
57
|
+
"e2e: end-to-end tests that spin up a fake gateway and exercise the full SDK loop",
|
|
58
|
+
"e2e_network: e2e tests that hit the real internet (gated by E2E_NETWORK=1)",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.ruff]
|
|
62
|
+
line-length = 100
|
|
63
|
+
target-version = "py310"
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint]
|
|
66
|
+
select = ["E", "F", "I", "B", "UP", "N", "ASYNC"]
|
|
67
|
+
ignore = ["E501"]
|
|
68
|
+
|
|
69
|
+
[tool.mypy]
|
|
70
|
+
python_version = "3.10"
|
|
71
|
+
strict = true
|
|
72
|
+
warn_unused_ignores = true
|
|
73
|
+
warn_redundant_casts = true
|