spatial-memory-mcp 1.9.1__py3-none-any.whl
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.
- spatial_memory/__init__.py +97 -0
- spatial_memory/__main__.py +271 -0
- spatial_memory/adapters/__init__.py +7 -0
- spatial_memory/adapters/lancedb_repository.py +880 -0
- spatial_memory/config.py +769 -0
- spatial_memory/core/__init__.py +118 -0
- spatial_memory/core/cache.py +317 -0
- spatial_memory/core/circuit_breaker.py +297 -0
- spatial_memory/core/connection_pool.py +220 -0
- spatial_memory/core/consolidation_strategies.py +401 -0
- spatial_memory/core/database.py +3072 -0
- spatial_memory/core/db_idempotency.py +242 -0
- spatial_memory/core/db_indexes.py +576 -0
- spatial_memory/core/db_migrations.py +588 -0
- spatial_memory/core/db_search.py +512 -0
- spatial_memory/core/db_versioning.py +178 -0
- spatial_memory/core/embeddings.py +558 -0
- spatial_memory/core/errors.py +317 -0
- spatial_memory/core/file_security.py +701 -0
- spatial_memory/core/filesystem.py +178 -0
- spatial_memory/core/health.py +289 -0
- spatial_memory/core/helpers.py +79 -0
- spatial_memory/core/import_security.py +433 -0
- spatial_memory/core/lifecycle_ops.py +1067 -0
- spatial_memory/core/logging.py +194 -0
- spatial_memory/core/metrics.py +192 -0
- spatial_memory/core/models.py +660 -0
- spatial_memory/core/rate_limiter.py +326 -0
- spatial_memory/core/response_types.py +500 -0
- spatial_memory/core/security.py +588 -0
- spatial_memory/core/spatial_ops.py +430 -0
- spatial_memory/core/tracing.py +300 -0
- spatial_memory/core/utils.py +110 -0
- spatial_memory/core/validation.py +406 -0
- spatial_memory/factory.py +444 -0
- spatial_memory/migrations/__init__.py +40 -0
- spatial_memory/ports/__init__.py +11 -0
- spatial_memory/ports/repositories.py +630 -0
- spatial_memory/py.typed +0 -0
- spatial_memory/server.py +1214 -0
- spatial_memory/services/__init__.py +70 -0
- spatial_memory/services/decay_manager.py +411 -0
- spatial_memory/services/export_import.py +1031 -0
- spatial_memory/services/lifecycle.py +1139 -0
- spatial_memory/services/memory.py +412 -0
- spatial_memory/services/spatial.py +1152 -0
- spatial_memory/services/utility.py +429 -0
- spatial_memory/tools/__init__.py +5 -0
- spatial_memory/tools/definitions.py +695 -0
- spatial_memory/verify.py +140 -0
- spatial_memory_mcp-1.9.1.dist-info/METADATA +509 -0
- spatial_memory_mcp-1.9.1.dist-info/RECORD +55 -0
- spatial_memory_mcp-1.9.1.dist-info/WHEEL +4 -0
- spatial_memory_mcp-1.9.1.dist-info/entry_points.txt +2 -0
- spatial_memory_mcp-1.9.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
"""Protocol interfaces for repository and embedding services.
|
|
2
|
+
|
|
3
|
+
These protocols define the contracts between the service layer and infrastructure.
|
|
4
|
+
Using typing.Protocol enables structural subtyping (duck typing with type checking).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Iterator
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Protocol
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from spatial_memory.core.models import Memory, MemoryResult
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MemoryRepositoryProtocol(Protocol):
|
|
19
|
+
"""Protocol for memory storage and retrieval operations.
|
|
20
|
+
|
|
21
|
+
Implementations must provide all methods defined here.
|
|
22
|
+
The LanceDBMemoryRepository is the primary implementation.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def add(self, memory: Memory, vector: np.ndarray) -> str:
|
|
26
|
+
"""Add a memory with its embedding vector.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
memory: The Memory object to store.
|
|
30
|
+
vector: The embedding vector for the memory.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
The generated memory ID (UUID string).
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
ValidationError: If input validation fails.
|
|
37
|
+
StorageError: If database operation fails.
|
|
38
|
+
"""
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
def add_batch(
|
|
42
|
+
self,
|
|
43
|
+
memories: list[Memory],
|
|
44
|
+
vectors: list[np.ndarray],
|
|
45
|
+
) -> list[str]:
|
|
46
|
+
"""Add multiple memories efficiently.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
memories: List of Memory objects to store.
|
|
50
|
+
vectors: List of embedding vectors (same order as memories).
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
List of generated memory IDs.
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
ValidationError: If input validation fails.
|
|
57
|
+
StorageError: If database operation fails.
|
|
58
|
+
"""
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
def get(self, memory_id: str) -> Memory | None:
|
|
62
|
+
"""Get a memory by ID.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
memory_id: The memory UUID.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
The Memory object, or None if not found.
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
ValidationError: If memory_id is invalid.
|
|
72
|
+
StorageError: If database operation fails.
|
|
73
|
+
"""
|
|
74
|
+
...
|
|
75
|
+
|
|
76
|
+
def get_with_vector(self, memory_id: str) -> tuple[Memory, np.ndarray] | None:
|
|
77
|
+
"""Get a memory and its vector by ID.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
memory_id: The memory UUID.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Tuple of (Memory, vector), or None if not found.
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
ValidationError: If memory_id is invalid.
|
|
87
|
+
StorageError: If database operation fails.
|
|
88
|
+
"""
|
|
89
|
+
...
|
|
90
|
+
|
|
91
|
+
def delete(self, memory_id: str) -> bool:
|
|
92
|
+
"""Delete a memory by ID.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
memory_id: The memory UUID.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
True if deleted, False if not found.
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
ValidationError: If memory_id is invalid.
|
|
102
|
+
StorageError: If database operation fails.
|
|
103
|
+
"""
|
|
104
|
+
...
|
|
105
|
+
|
|
106
|
+
def delete_batch(self, memory_ids: list[str]) -> tuple[int, list[str]]:
|
|
107
|
+
"""Delete multiple memories.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
memory_ids: List of memory UUIDs to delete.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Tuple of (count_deleted, list_of_deleted_ids) where:
|
|
114
|
+
- count_deleted: Number of memories actually deleted
|
|
115
|
+
- list_of_deleted_ids: IDs that were actually deleted
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
ValidationError: If any memory_id is invalid.
|
|
119
|
+
StorageError: If database operation fails.
|
|
120
|
+
"""
|
|
121
|
+
...
|
|
122
|
+
|
|
123
|
+
def search(
|
|
124
|
+
self,
|
|
125
|
+
query_vector: np.ndarray,
|
|
126
|
+
limit: int = 5,
|
|
127
|
+
namespace: str | None = None,
|
|
128
|
+
include_vector: bool = False,
|
|
129
|
+
) -> list[MemoryResult]:
|
|
130
|
+
"""Search for similar memories by vector.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
query_vector: Query embedding vector.
|
|
134
|
+
limit: Maximum number of results.
|
|
135
|
+
namespace: Filter to specific namespace.
|
|
136
|
+
include_vector: Whether to include embedding vectors in results.
|
|
137
|
+
Defaults to False to reduce response size.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
List of MemoryResult objects with similarity scores.
|
|
141
|
+
If include_vector=True, each result includes its embedding vector.
|
|
142
|
+
|
|
143
|
+
Raises:
|
|
144
|
+
ValidationError: If input validation fails.
|
|
145
|
+
StorageError: If database operation fails.
|
|
146
|
+
"""
|
|
147
|
+
...
|
|
148
|
+
|
|
149
|
+
def update_access(self, memory_id: str) -> None:
|
|
150
|
+
"""Update access timestamp and count for a memory.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
memory_id: The memory UUID.
|
|
154
|
+
|
|
155
|
+
Raises:
|
|
156
|
+
ValidationError: If memory_id is invalid.
|
|
157
|
+
MemoryNotFoundError: If memory doesn't exist.
|
|
158
|
+
StorageError: If database operation fails.
|
|
159
|
+
"""
|
|
160
|
+
...
|
|
161
|
+
|
|
162
|
+
def update_access_batch(self, memory_ids: list[str]) -> int:
|
|
163
|
+
"""Update access timestamp and count for multiple memories.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
memory_ids: List of memory UUIDs.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Number of memories successfully updated.
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
ValidationError: If any memory_id is invalid.
|
|
173
|
+
StorageError: If database operation fails.
|
|
174
|
+
"""
|
|
175
|
+
...
|
|
176
|
+
|
|
177
|
+
def update(self, memory_id: str, updates: dict[str, Any]) -> None:
|
|
178
|
+
"""Update a memory's fields.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
memory_id: The memory UUID.
|
|
182
|
+
updates: Fields to update.
|
|
183
|
+
|
|
184
|
+
Raises:
|
|
185
|
+
ValidationError: If input validation fails.
|
|
186
|
+
MemoryNotFoundError: If memory doesn't exist.
|
|
187
|
+
StorageError: If database operation fails.
|
|
188
|
+
"""
|
|
189
|
+
...
|
|
190
|
+
|
|
191
|
+
def get_batch(self, memory_ids: list[str]) -> dict[str, Memory]:
|
|
192
|
+
"""Get multiple memories by ID in a single query.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
memory_ids: List of memory UUIDs to retrieve.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Dict mapping memory_id to Memory object. Missing IDs are not included.
|
|
199
|
+
|
|
200
|
+
Raises:
|
|
201
|
+
ValidationError: If any memory_id format is invalid.
|
|
202
|
+
StorageError: If database operation fails.
|
|
203
|
+
"""
|
|
204
|
+
...
|
|
205
|
+
|
|
206
|
+
def update_batch(
|
|
207
|
+
self, updates: list[tuple[str, dict[str, Any]]]
|
|
208
|
+
) -> tuple[int, list[str]]:
|
|
209
|
+
"""Update multiple memories in a single batch operation.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
updates: List of (memory_id, updates_dict) tuples.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Tuple of (success_count, list of failed memory_ids).
|
|
216
|
+
|
|
217
|
+
Raises:
|
|
218
|
+
StorageError: If database operation fails completely.
|
|
219
|
+
"""
|
|
220
|
+
...
|
|
221
|
+
|
|
222
|
+
def count(self, namespace: str | None = None) -> int:
|
|
223
|
+
"""Count memories.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
namespace: Filter to specific namespace.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Number of memories.
|
|
230
|
+
|
|
231
|
+
Raises:
|
|
232
|
+
ValidationError: If namespace is invalid.
|
|
233
|
+
StorageError: If database operation fails.
|
|
234
|
+
"""
|
|
235
|
+
...
|
|
236
|
+
|
|
237
|
+
def get_namespaces(self) -> list[str]:
|
|
238
|
+
"""Get all unique namespaces.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
List of namespace names.
|
|
242
|
+
|
|
243
|
+
Raises:
|
|
244
|
+
StorageError: If database operation fails.
|
|
245
|
+
"""
|
|
246
|
+
...
|
|
247
|
+
|
|
248
|
+
def get_all(
|
|
249
|
+
self,
|
|
250
|
+
namespace: str | None = None,
|
|
251
|
+
limit: int | None = None,
|
|
252
|
+
) -> list[tuple[Memory, np.ndarray]]:
|
|
253
|
+
"""Get all memories with their vectors.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
namespace: Filter to specific namespace.
|
|
257
|
+
limit: Maximum number of results.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
List of (Memory, vector) tuples.
|
|
261
|
+
|
|
262
|
+
Raises:
|
|
263
|
+
ValidationError: If namespace is invalid.
|
|
264
|
+
StorageError: If database operation fails.
|
|
265
|
+
"""
|
|
266
|
+
...
|
|
267
|
+
|
|
268
|
+
def hybrid_search(
|
|
269
|
+
self,
|
|
270
|
+
query_vector: np.ndarray,
|
|
271
|
+
query_text: str,
|
|
272
|
+
limit: int = 5,
|
|
273
|
+
namespace: str | None = None,
|
|
274
|
+
alpha: float = 0.5,
|
|
275
|
+
) -> list[MemoryResult]:
|
|
276
|
+
"""Search using both vector similarity and full-text search.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
query_vector: Query embedding vector.
|
|
280
|
+
query_text: Query text for FTS.
|
|
281
|
+
limit: Maximum results.
|
|
282
|
+
namespace: Optional namespace filter.
|
|
283
|
+
alpha: Balance between vector (1.0) and FTS (0.0).
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
List of matching memories ranked by combined score.
|
|
287
|
+
|
|
288
|
+
Raises:
|
|
289
|
+
ValidationError: If input validation fails.
|
|
290
|
+
StorageError: If database operation fails.
|
|
291
|
+
"""
|
|
292
|
+
...
|
|
293
|
+
|
|
294
|
+
def get_health_metrics(self) -> dict[str, Any]:
|
|
295
|
+
"""Get database health metrics.
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
Dictionary with health metrics.
|
|
299
|
+
|
|
300
|
+
Raises:
|
|
301
|
+
StorageError: If database operation fails.
|
|
302
|
+
"""
|
|
303
|
+
...
|
|
304
|
+
|
|
305
|
+
def optimize(self) -> dict[str, Any]:
|
|
306
|
+
"""Run optimization and compaction.
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
Dictionary with optimization results.
|
|
310
|
+
|
|
311
|
+
Raises:
|
|
312
|
+
StorageError: If database operation fails.
|
|
313
|
+
"""
|
|
314
|
+
...
|
|
315
|
+
|
|
316
|
+
def export_to_parquet(self, path: Path) -> int:
|
|
317
|
+
"""Export memories to Parquet file.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
path: Output file path.
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
Number of records exported.
|
|
324
|
+
|
|
325
|
+
Raises:
|
|
326
|
+
StorageError: If export fails.
|
|
327
|
+
"""
|
|
328
|
+
...
|
|
329
|
+
|
|
330
|
+
def import_from_parquet(
|
|
331
|
+
self,
|
|
332
|
+
path: Path,
|
|
333
|
+
namespace_override: str | None = None,
|
|
334
|
+
) -> int:
|
|
335
|
+
"""Import memories from Parquet file.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
path: Input file path.
|
|
339
|
+
namespace_override: Override namespace for imported memories.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
Number of records imported.
|
|
343
|
+
|
|
344
|
+
Raises:
|
|
345
|
+
ValidationError: If input validation fails.
|
|
346
|
+
StorageError: If import fails.
|
|
347
|
+
"""
|
|
348
|
+
...
|
|
349
|
+
|
|
350
|
+
def get_vectors_for_clustering(
|
|
351
|
+
self,
|
|
352
|
+
namespace: str | None = None,
|
|
353
|
+
max_memories: int = 10_000,
|
|
354
|
+
) -> tuple[list[str], np.ndarray]:
|
|
355
|
+
"""Extract memory IDs and vectors efficiently for clustering.
|
|
356
|
+
|
|
357
|
+
Optimized for memory efficiency with large datasets. Used by
|
|
358
|
+
spatial operations like HDBSCAN clustering for region detection.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
namespace: Filter to specific namespace.
|
|
362
|
+
max_memories: Maximum memories to fetch.
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
Tuple of (memory_ids, vectors_array) where vectors_array
|
|
366
|
+
is a 2D numpy array of shape (n_memories, embedding_dim).
|
|
367
|
+
|
|
368
|
+
Raises:
|
|
369
|
+
ValidationError: If input validation fails.
|
|
370
|
+
StorageError: If database operation fails.
|
|
371
|
+
"""
|
|
372
|
+
...
|
|
373
|
+
|
|
374
|
+
def batch_vector_search(
|
|
375
|
+
self,
|
|
376
|
+
query_vectors: list[np.ndarray],
|
|
377
|
+
limit_per_query: int = 3,
|
|
378
|
+
namespace: str | None = None,
|
|
379
|
+
include_vector: bool = False,
|
|
380
|
+
) -> list[list[dict[str, Any]]]:
|
|
381
|
+
"""Search for memories near multiple query points.
|
|
382
|
+
|
|
383
|
+
Efficient for operations like journey interpolation where multiple
|
|
384
|
+
points need to find nearby memories. Supports parallel execution.
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
query_vectors: List of query embedding vectors.
|
|
388
|
+
limit_per_query: Maximum results per query vector.
|
|
389
|
+
namespace: Filter to specific namespace.
|
|
390
|
+
include_vector: Whether to include embedding vectors in results.
|
|
391
|
+
Defaults to False to reduce response size.
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
List of result lists (one per query vector). Each result
|
|
395
|
+
is a dict containing memory fields and similarity score.
|
|
396
|
+
If include_vector=True, each dict includes the 'vector' field.
|
|
397
|
+
|
|
398
|
+
Raises:
|
|
399
|
+
ValidationError: If input validation fails.
|
|
400
|
+
StorageError: If database operation fails.
|
|
401
|
+
"""
|
|
402
|
+
...
|
|
403
|
+
|
|
404
|
+
def vector_search(
|
|
405
|
+
self,
|
|
406
|
+
query_vector: np.ndarray,
|
|
407
|
+
limit: int = 5,
|
|
408
|
+
namespace: str | None = None,
|
|
409
|
+
) -> list[dict[str, Any]]:
|
|
410
|
+
"""Search for similar memories by vector (returns raw dict).
|
|
411
|
+
|
|
412
|
+
Lower-level search that returns raw dictionary results instead
|
|
413
|
+
of MemoryResult objects. Useful for spatial operations that need
|
|
414
|
+
direct access to all fields including vectors.
|
|
415
|
+
|
|
416
|
+
Args:
|
|
417
|
+
query_vector: Query embedding vector.
|
|
418
|
+
limit: Maximum number of results.
|
|
419
|
+
namespace: Filter to specific namespace.
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
List of memory records as dictionaries with similarity scores.
|
|
423
|
+
|
|
424
|
+
Raises:
|
|
425
|
+
ValidationError: If input validation fails.
|
|
426
|
+
StorageError: If database operation fails.
|
|
427
|
+
"""
|
|
428
|
+
...
|
|
429
|
+
|
|
430
|
+
# -------------------------------------------------------------------------
|
|
431
|
+
# Phase 5 Protocol Extensions: Utility & Export/Import Operations
|
|
432
|
+
# -------------------------------------------------------------------------
|
|
433
|
+
|
|
434
|
+
def delete_by_namespace(self, namespace: str) -> int:
|
|
435
|
+
"""Delete all memories in a namespace.
|
|
436
|
+
|
|
437
|
+
Removes all memories belonging to the specified namespace from storage.
|
|
438
|
+
This is a destructive operation that cannot be undone.
|
|
439
|
+
|
|
440
|
+
Args:
|
|
441
|
+
namespace: The namespace whose memories should be deleted.
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
Number of memories deleted.
|
|
445
|
+
|
|
446
|
+
Raises:
|
|
447
|
+
ValidationError: If namespace is invalid.
|
|
448
|
+
StorageError: If database operation fails.
|
|
449
|
+
"""
|
|
450
|
+
...
|
|
451
|
+
|
|
452
|
+
def rename_namespace(self, old_namespace: str, new_namespace: str) -> int:
|
|
453
|
+
"""Rename all memories from one namespace to another.
|
|
454
|
+
|
|
455
|
+
Atomically updates the namespace field for all memories belonging
|
|
456
|
+
to the source namespace. Uses batch updates for data integrity.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
old_namespace: The current namespace name (source).
|
|
460
|
+
new_namespace: The new namespace name (target).
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
Number of memories renamed.
|
|
464
|
+
|
|
465
|
+
Raises:
|
|
466
|
+
ValidationError: If namespace names are invalid.
|
|
467
|
+
NamespaceNotFoundError: If old_namespace doesn't exist.
|
|
468
|
+
StorageError: If database operation fails.
|
|
469
|
+
"""
|
|
470
|
+
...
|
|
471
|
+
|
|
472
|
+
def get_stats(self, namespace: str | None = None) -> dict[str, Any]:
|
|
473
|
+
"""Get comprehensive database statistics.
|
|
474
|
+
|
|
475
|
+
Retrieves statistics about the memory database including total counts,
|
|
476
|
+
storage size, index information, and health metrics. Uses efficient
|
|
477
|
+
database-level aggregation queries.
|
|
478
|
+
|
|
479
|
+
Args:
|
|
480
|
+
namespace: Filter statistics to a specific namespace.
|
|
481
|
+
If None, returns statistics for all namespaces.
|
|
482
|
+
|
|
483
|
+
Returns:
|
|
484
|
+
Dictionary containing:
|
|
485
|
+
- total_memories: Total count of memories
|
|
486
|
+
- memories_by_namespace: Dict mapping namespace to count
|
|
487
|
+
- storage_bytes: Total storage size in bytes
|
|
488
|
+
- storage_mb: Total storage size in megabytes
|
|
489
|
+
- has_vector_index: Whether vector index exists
|
|
490
|
+
- has_fts_index: Whether full-text search index exists
|
|
491
|
+
- num_fragments: Number of storage fragments
|
|
492
|
+
- needs_compaction: Whether compaction is recommended
|
|
493
|
+
- table_version: Current table version number
|
|
494
|
+
- indices: List of index information dicts
|
|
495
|
+
|
|
496
|
+
Raises:
|
|
497
|
+
ValidationError: If namespace is invalid.
|
|
498
|
+
StorageError: If database operation fails.
|
|
499
|
+
"""
|
|
500
|
+
...
|
|
501
|
+
|
|
502
|
+
def get_namespace_stats(self, namespace: str) -> dict[str, Any]:
|
|
503
|
+
"""Get statistics for a specific namespace.
|
|
504
|
+
|
|
505
|
+
Retrieves detailed statistics for a single namespace including
|
|
506
|
+
memory count, date ranges, and storage estimates.
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
namespace: The namespace to get statistics for.
|
|
510
|
+
|
|
511
|
+
Returns:
|
|
512
|
+
Dictionary containing:
|
|
513
|
+
- namespace: The namespace name
|
|
514
|
+
- memory_count: Number of memories in namespace
|
|
515
|
+
- oldest_memory: Datetime of oldest memory (or None)
|
|
516
|
+
- newest_memory: Datetime of newest memory (or None)
|
|
517
|
+
- avg_content_length: Average content length (optional)
|
|
518
|
+
|
|
519
|
+
Raises:
|
|
520
|
+
ValidationError: If namespace is invalid.
|
|
521
|
+
NamespaceNotFoundError: If namespace doesn't exist.
|
|
522
|
+
StorageError: If database operation fails.
|
|
523
|
+
"""
|
|
524
|
+
...
|
|
525
|
+
|
|
526
|
+
def get_all_for_export(
|
|
527
|
+
self,
|
|
528
|
+
namespace: str | None = None,
|
|
529
|
+
batch_size: int = 1000,
|
|
530
|
+
) -> Iterator[list[dict[str, Any]]]:
|
|
531
|
+
"""Stream all memories for export in batches.
|
|
532
|
+
|
|
533
|
+
Memory-efficient export using generator pattern. Yields batches
|
|
534
|
+
of memory records suitable for writing to export files. This method
|
|
535
|
+
handles only data retrieval; file I/O is the service layer's concern.
|
|
536
|
+
|
|
537
|
+
Args:
|
|
538
|
+
namespace: Filter to a specific namespace.
|
|
539
|
+
If None, exports all namespaces.
|
|
540
|
+
batch_size: Number of records per yielded batch.
|
|
541
|
+
|
|
542
|
+
Yields:
|
|
543
|
+
Batches of memory dictionaries. Each dictionary contains all
|
|
544
|
+
memory fields including the embedding vector.
|
|
545
|
+
|
|
546
|
+
Raises:
|
|
547
|
+
ValidationError: If namespace is invalid.
|
|
548
|
+
StorageError: If database operation fails.
|
|
549
|
+
"""
|
|
550
|
+
...
|
|
551
|
+
|
|
552
|
+
def bulk_import(
|
|
553
|
+
self,
|
|
554
|
+
records: Iterator[dict[str, Any]],
|
|
555
|
+
batch_size: int = 1000,
|
|
556
|
+
namespace_override: str | None = None,
|
|
557
|
+
) -> tuple[int, list[str]]:
|
|
558
|
+
"""Import memories from an iterator of records.
|
|
559
|
+
|
|
560
|
+
Supports streaming import for large datasets. Accepts pre-parsed
|
|
561
|
+
and pre-validated records; file parsing and validation are handled
|
|
562
|
+
by the service layer.
|
|
563
|
+
|
|
564
|
+
Args:
|
|
565
|
+
records: Iterator of memory dictionaries. Each dictionary
|
|
566
|
+
should contain required fields (content, vector, etc.).
|
|
567
|
+
batch_size: Number of records per database insert batch.
|
|
568
|
+
namespace_override: If provided, overrides the namespace
|
|
569
|
+
field for all imported records.
|
|
570
|
+
|
|
571
|
+
Returns:
|
|
572
|
+
Tuple of (records_imported, list_of_new_ids) where:
|
|
573
|
+
- records_imported: Total number of records successfully imported
|
|
574
|
+
- list_of_new_ids: List of generated memory IDs
|
|
575
|
+
|
|
576
|
+
Raises:
|
|
577
|
+
ValidationError: If records contain invalid data.
|
|
578
|
+
StorageError: If database operation fails.
|
|
579
|
+
"""
|
|
580
|
+
...
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
class EmbeddingServiceProtocol(Protocol):
|
|
584
|
+
"""Protocol for text embedding generation.
|
|
585
|
+
|
|
586
|
+
Implementations can use local models (sentence-transformers)
|
|
587
|
+
or API-based services (OpenAI).
|
|
588
|
+
"""
|
|
589
|
+
|
|
590
|
+
@property
|
|
591
|
+
def dimensions(self) -> int:
|
|
592
|
+
"""Get the embedding dimensions."""
|
|
593
|
+
...
|
|
594
|
+
|
|
595
|
+
@property
|
|
596
|
+
def backend(self) -> str:
|
|
597
|
+
"""Get the active embedding backend.
|
|
598
|
+
|
|
599
|
+
Returns:
|
|
600
|
+
'openai' for OpenAI API, 'onnx' or 'pytorch' for local models.
|
|
601
|
+
"""
|
|
602
|
+
...
|
|
603
|
+
|
|
604
|
+
def embed(self, text: str) -> np.ndarray:
|
|
605
|
+
"""Generate embedding for a single text.
|
|
606
|
+
|
|
607
|
+
Args:
|
|
608
|
+
text: Text to embed.
|
|
609
|
+
|
|
610
|
+
Returns:
|
|
611
|
+
Embedding vector as numpy array.
|
|
612
|
+
|
|
613
|
+
Raises:
|
|
614
|
+
EmbeddingError: If embedding generation fails.
|
|
615
|
+
"""
|
|
616
|
+
...
|
|
617
|
+
|
|
618
|
+
def embed_batch(self, texts: list[str]) -> list[np.ndarray]:
|
|
619
|
+
"""Generate embeddings for multiple texts.
|
|
620
|
+
|
|
621
|
+
Args:
|
|
622
|
+
texts: List of texts to embed.
|
|
623
|
+
|
|
624
|
+
Returns:
|
|
625
|
+
List of embedding vectors.
|
|
626
|
+
|
|
627
|
+
Raises:
|
|
628
|
+
EmbeddingError: If embedding generation fails.
|
|
629
|
+
"""
|
|
630
|
+
...
|
spatial_memory/py.typed
ADDED
|
File without changes
|