muninn-python 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.
- muninn_python-0.1.0/.gitignore +17 -0
- muninn_python-0.1.0/PKG-INFO +373 -0
- muninn_python-0.1.0/README.md +339 -0
- muninn_python-0.1.0/muninn/__init__.py +57 -0
- muninn_python-0.1.0/muninn/client.py +499 -0
- muninn_python-0.1.0/muninn/errors.py +47 -0
- muninn_python-0.1.0/muninn/langchain.py +184 -0
- muninn_python-0.1.0/muninn/sse.py +122 -0
- muninn_python-0.1.0/muninn/types.py +132 -0
- muninn_python-0.1.0/pyproject.toml +66 -0
- muninn_python-0.1.0/requirements.txt +2 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: muninn-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for MuninnDB — the cognitive memory database
|
|
5
|
+
Project-URL: Homepage, https://muninndb.com
|
|
6
|
+
Project-URL: Repository, https://github.com/scrypster/muninndb
|
|
7
|
+
Project-URL: Documentation, https://github.com/scrypster/muninndb/blob/main/sdk/python/README.md
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/scrypster/muninndb/issues
|
|
9
|
+
Author-email: MuninnDB <hello@muninndb.com>
|
|
10
|
+
License: Apache-2.0
|
|
11
|
+
Keywords: agent-memory,ai,cognitive,database,embeddings,langchain,llm,memory,semantic-search,vector-database
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software 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: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Database
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
24
|
+
Requires-Dist: python-dotenv>=1.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: langchain-anthropic>=0.3; extra == 'dev'
|
|
27
|
+
Requires-Dist: langchain-core>=0.3; extra == 'dev'
|
|
28
|
+
Requires-Dist: langchain>=0.3; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
31
|
+
Provides-Extra: langchain
|
|
32
|
+
Requires-Dist: langchain-core>=0.3; extra == 'langchain'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# MuninnDB Python SDK
|
|
36
|
+
|
|
37
|
+
An async-first Python client for **MuninnDB**, a cognitive memory database with semantic search, graph traversal, and real-time subscriptions.
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- **Async/await throughout** — Built on `httpx` for concurrent, non-blocking operations
|
|
42
|
+
- **Semantic memory activation** — Query memories by meaning, not keywords
|
|
43
|
+
- **Graph associations** — Link engrams and traverse relationships
|
|
44
|
+
- **Real-time subscriptions** — Server-Sent Events (SSE) with auto-reconnect
|
|
45
|
+
- **Automatic retry logic** — Exponential backoff with jitter for transient failures
|
|
46
|
+
- **Type-safe** — Full type hints for IDE support and runtime validation
|
|
47
|
+
- **Connection pooling** — Configurable keepalive and concurrent connections
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
### Requirements
|
|
52
|
+
- Python 3.11+
|
|
53
|
+
|
|
54
|
+
### Setup
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install -r requirements.txt
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quick Start
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import asyncio
|
|
64
|
+
from muninn import MuninnClient
|
|
65
|
+
|
|
66
|
+
async def main():
|
|
67
|
+
async with MuninnClient("http://localhost:8476") as client:
|
|
68
|
+
# Write a memory
|
|
69
|
+
engram_id = await client.write(
|
|
70
|
+
vault="default",
|
|
71
|
+
concept="neural plasticity",
|
|
72
|
+
content="The brain's ability to reorganize neural connections.",
|
|
73
|
+
tags=["neuroscience", "learning"]
|
|
74
|
+
)
|
|
75
|
+
print(f"Created: {engram_id}")
|
|
76
|
+
|
|
77
|
+
# Activate memory (semantic search)
|
|
78
|
+
results = await client.activate(
|
|
79
|
+
vault="default",
|
|
80
|
+
context=["how does learning work?"],
|
|
81
|
+
max_results=10
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
for item in results.activations:
|
|
85
|
+
print(f"[{item.score:.2f}] {item.concept}")
|
|
86
|
+
|
|
87
|
+
asyncio.run(main())
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## API Reference
|
|
91
|
+
|
|
92
|
+
### Core Methods
|
|
93
|
+
|
|
94
|
+
#### `write(vault, concept, content, tags=None, confidence=0.9, stability=0.5) → str`
|
|
95
|
+
|
|
96
|
+
Write an engram (memory) to the database.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
engram_id = await client.write(
|
|
100
|
+
vault="default",
|
|
101
|
+
concept="example",
|
|
102
|
+
content="Long-form content",
|
|
103
|
+
tags=["tag1", "tag2"],
|
|
104
|
+
confidence=0.95,
|
|
105
|
+
stability=0.8
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Returns:** ULID string ID of created engram
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
#### `activate(vault, context, max_results=10, threshold=0.1, brief_mode="auto") → ActivateResponse`
|
|
114
|
+
|
|
115
|
+
Activate memory using semantic search and optional graph traversal.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
result = await client.activate(
|
|
119
|
+
vault="default",
|
|
120
|
+
context=["query", "terms"],
|
|
121
|
+
max_results=10,
|
|
122
|
+
threshold=0.1,
|
|
123
|
+
brief_mode="extractive" # "auto", "extractive", "abstractive"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
for item in result.activations:
|
|
127
|
+
print(f"Score: {item.score}, Concept: {item.concept}")
|
|
128
|
+
|
|
129
|
+
for sentence in result.brief or []:
|
|
130
|
+
print(f"Brief: {sentence.text}")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Returns:** `ActivateResponse` with:
|
|
134
|
+
- `query_id` — Query identifier
|
|
135
|
+
- `total_found` — Total matching engrams
|
|
136
|
+
- `activations` — List of `ActivationItem` (id, concept, content, score, confidence, why, hop_path, dormant)
|
|
137
|
+
- `latency_ms` — Query latency
|
|
138
|
+
- `brief` — Optional extractive/abstractive summary
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
#### `read(id, vault="default") → ReadResponse`
|
|
143
|
+
|
|
144
|
+
Read a specific engram by ID.
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
engram = await client.read("01JM2345...", vault="default")
|
|
148
|
+
print(engram.concept, engram.confidence)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Returns:** `ReadResponse` with full engram details
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
#### `forget(id, vault="default", hard=False) → bool`
|
|
156
|
+
|
|
157
|
+
Delete an engram (soft or hard).
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
# Soft delete (recoverable)
|
|
161
|
+
await client.forget(engram_id, vault="default")
|
|
162
|
+
|
|
163
|
+
# Hard delete (permanent)
|
|
164
|
+
await client.forget(engram_id, vault="default", hard=True)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Returns:** `True` on success
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
#### `link(source_id, target_id, vault="default", rel_type=5, weight=1.0) → bool`
|
|
172
|
+
|
|
173
|
+
Create an association between two engrams.
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
await client.link(
|
|
177
|
+
source_id="01JM...",
|
|
178
|
+
target_id="01JM...",
|
|
179
|
+
vault="default",
|
|
180
|
+
rel_type=5,
|
|
181
|
+
weight=0.9
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Returns:** `True` on success
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
#### `stats() → StatResponse`
|
|
190
|
+
|
|
191
|
+
Get database statistics and coherence metrics.
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
stats = await client.stats()
|
|
195
|
+
print(f"Engrams: {stats.engram_count}")
|
|
196
|
+
print(f"Storage: {stats.storage_bytes} bytes")
|
|
197
|
+
|
|
198
|
+
if stats.coherence:
|
|
199
|
+
for vault_name, coherence in stats.coherence.items():
|
|
200
|
+
print(f"Vault {vault_name} coherence: {coherence.score:.2f}")
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Returns:** `StatResponse` with engram_count, vault_count, storage_bytes, and coherence dict
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
#### `subscribe(vault="default", push_on_write=True, threshold=0.0) → SSEStream`
|
|
208
|
+
|
|
209
|
+
Subscribe to real-time vault events via Server-Sent Events.
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
stream = client.subscribe(vault="default", push_on_write=True)
|
|
213
|
+
async for push in stream:
|
|
214
|
+
print(f"New engram: {push.engram_id}")
|
|
215
|
+
if condition:
|
|
216
|
+
await stream.close()
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Returns:** Async iterable yielding `Push` events with:
|
|
220
|
+
- `subscription_id` — Subscription ID
|
|
221
|
+
- `trigger` — Event type ("new_write", etc.)
|
|
222
|
+
- `push_number` — Push sequence number
|
|
223
|
+
- `engram_id` — ID of written engram
|
|
224
|
+
- `at` — Unix timestamp
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
#### `health() → bool`
|
|
229
|
+
|
|
230
|
+
Check if MuninnDB server is reachable and healthy.
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
if await client.health():
|
|
234
|
+
print("Server OK")
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Returns:** `True` if server responds with 200 OK
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### Configuration
|
|
242
|
+
|
|
243
|
+
Create a client with custom settings:
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
client = MuninnClient(
|
|
247
|
+
base_url="http://localhost:8476", # Server address
|
|
248
|
+
token="your-bearer-token", # Optional auth token
|
|
249
|
+
timeout=5.0, # Request timeout (seconds)
|
|
250
|
+
max_retries=3, # Max retry attempts
|
|
251
|
+
retry_backoff=0.5, # Initial backoff multiplier
|
|
252
|
+
max_connections=20, # Max concurrent connections
|
|
253
|
+
keepalive_connections=10 # Max keepalive pool size
|
|
254
|
+
)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Error Handling
|
|
258
|
+
|
|
259
|
+
The SDK raises semantic error types:
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from muninn import (
|
|
263
|
+
MuninnError, # Base error
|
|
264
|
+
MuninnAuthError, # 401 Unauthorized
|
|
265
|
+
MuninnNotFound, # 404 Not Found
|
|
266
|
+
MuninnConflict, # 409 Conflict
|
|
267
|
+
MuninnServerError, # 5xx Server errors
|
|
268
|
+
MuninnConnectionError, # Network errors
|
|
269
|
+
MuninnTimeoutError # Request timeout
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
async with MuninnClient() as client:
|
|
273
|
+
try:
|
|
274
|
+
result = await client.activate(vault="default", context=["query"])
|
|
275
|
+
except MuninnAuthError:
|
|
276
|
+
print("Invalid token")
|
|
277
|
+
except MuninnNotFound:
|
|
278
|
+
print("Vault not found")
|
|
279
|
+
except MuninnConnectionError:
|
|
280
|
+
print("Network error - will retry automatically")
|
|
281
|
+
except MuninnError as e:
|
|
282
|
+
print(f"Error: {e.status_code} - {e}")
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Examples
|
|
286
|
+
|
|
287
|
+
### Example 1: Write and Activate
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
python examples/write_activate.py
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Writes multiple neuroscience engrams and activates them with a query.
|
|
294
|
+
|
|
295
|
+
### Example 2: Real-Time Subscriptions
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
python examples/subscribe.py
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Subscribes to a vault and writes an engram, demonstrating SSE push events.
|
|
302
|
+
|
|
303
|
+
### Example 3: Cognitive Loop
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
python examples/cognitive_loop.py
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Full workflow: write → activate → link → inspect coherence.
|
|
310
|
+
|
|
311
|
+
## Retry Logic
|
|
312
|
+
|
|
313
|
+
The client automatically retries transient failures:
|
|
314
|
+
|
|
315
|
+
- **Retried errors:** 502, 503, 504, network errors, timeouts
|
|
316
|
+
- **Not retried:** 4xx client errors
|
|
317
|
+
- **Backoff:** Exponential with jitter: `retry_backoff * (2^attempt) + random(0, 0.1)`
|
|
318
|
+
- **Max attempts:** Configured via `max_retries` (default: 3)
|
|
319
|
+
|
|
320
|
+
Example with custom retry settings:
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
async with MuninnClient(
|
|
324
|
+
base_url="http://localhost:8476",
|
|
325
|
+
max_retries=5,
|
|
326
|
+
retry_backoff=1.0
|
|
327
|
+
) as client:
|
|
328
|
+
# This will retry up to 5 times with longer delays
|
|
329
|
+
result = await client.activate(vault="default", context=["query"])
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Type System
|
|
333
|
+
|
|
334
|
+
Full type hints enable IDE autocomplete:
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
from muninn import (
|
|
338
|
+
MuninnClient,
|
|
339
|
+
ActivateResponse,
|
|
340
|
+
ActivationItem,
|
|
341
|
+
ReadResponse,
|
|
342
|
+
StatResponse,
|
|
343
|
+
Push
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
async with MuninnClient() as client:
|
|
347
|
+
result: ActivateResponse = await client.activate(vault="default", context=[])
|
|
348
|
+
for item: ActivationItem in result.activations:
|
|
349
|
+
print(item.score, item.concept)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Performance Tips
|
|
353
|
+
|
|
354
|
+
1. **Reuse client:** Create one `MuninnClient` and reuse it for multiple operations
|
|
355
|
+
2. **Batch operations:** Use concurrent tasks for parallel writes/activations
|
|
356
|
+
3. **Connection pooling:** Adjust `max_connections` and `keepalive_connections` based on workload
|
|
357
|
+
4. **Timeout tuning:** Increase `timeout` for large activation queries
|
|
358
|
+
|
|
359
|
+
```python
|
|
360
|
+
import asyncio
|
|
361
|
+
|
|
362
|
+
async with MuninnClient() as client:
|
|
363
|
+
# Parallel writes
|
|
364
|
+
tasks = [
|
|
365
|
+
client.write(vault="default", concept=f"item-{i}", content="...")
|
|
366
|
+
for i in range(100)
|
|
367
|
+
]
|
|
368
|
+
ids = await asyncio.gather(*tasks)
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## License
|
|
372
|
+
|
|
373
|
+
MIT
|